Beispiel #1
0
func opResponsesSetter(op *spec.Operation) func(*spec.Response, map[int]spec.Response) {
	return func(def *spec.Response, scr map[int]spec.Response) {
		if op.Responses == nil {
			op.Responses = new(spec.Responses)
		}
		op.Responses.Default = def
		op.Responses.StatusCodeResponses = scr
	}
}
Beispiel #2
0
func (pp *paramStructParser) parseStructType(gofile *ast.File, operation *spec.Operation, tpe *ast.StructType, seenPreviously map[string]spec.Parameter) error {
	if tpe.Fields != nil {
		pt := seenPreviously

		for _, fld := range tpe.Fields.List {
			if len(fld.Names) == 0 {
				// when the embedded struct is annotated with swagger:allOf it will be used as allOf property
				// otherwise the fields will just be included as normal properties
				if err := pp.parseEmbeddedStruct(gofile, operation, fld.Type, pt); err != nil {
					return err
				}
			}
		}

		for _, fld := range tpe.Fields.List {
			var nm, gnm string
			if len(fld.Names) > 0 && fld.Names[0] != nil && fld.Names[0].IsExported() {
				nm = fld.Names[0].Name
				gnm = nm
				if fld.Tag != nil && len(strings.TrimSpace(fld.Tag.Value)) > 0 {
					tv, err := strconv.Unquote(fld.Tag.Value)
					if err != nil {
						return err
					}

					if strings.TrimSpace(tv) != "" {
						st := reflect.StructTag(tv)
						jsonTag := st.Get("json")
						if jsonTag == "-" {
							continue
						}
						if jsonTag != "" {
							nm = strings.Split(jsonTag, ",")[0]
						}
					}
				}

				in := "query"
				// scan for param location first, this changes some behavior down the line
				if fld.Doc != nil {
					for _, cmt := range fld.Doc.List {
						for _, line := range strings.Split(cmt.Text, "\n") {
							matches := rxIn.FindStringSubmatch(line)
							if len(matches) > 0 && len(strings.TrimSpace(matches[1])) > 0 {
								in = strings.TrimSpace(matches[1])
							}
						}
					}
				}

				ps := pt[nm]
				ps.In = in
				var pty swaggerTypable = paramTypable{&ps}
				if in == "body" {
					pty = schemaTypable{pty.Schema(), 0}
				}
				if in == "formData" && fld.Doc != nil && fileParam(fld.Doc) {
					pty.Typed("file", "")
				} else {
					if err := parseProperty(pp.scp, gofile, fld.Type, pty); err != nil {
						return err
					}
				}

				sp := new(sectionedParser)
				sp.setDescription = func(lines []string) { ps.Description = joinDropLast(lines) }
				if ps.Ref.String() == "" {
					sp.taggers = []tagParser{
						newSingleLineTagParser("maximum", &setMaximum{paramValidations{&ps}, rxf(rxMaximumFmt, "")}),
						newSingleLineTagParser("minimum", &setMinimum{paramValidations{&ps}, rxf(rxMinimumFmt, "")}),
						newSingleLineTagParser("multipleOf", &setMultipleOf{paramValidations{&ps}, rxf(rxMultipleOfFmt, "")}),
						newSingleLineTagParser("minLength", &setMinLength{paramValidations{&ps}, rxf(rxMinLengthFmt, "")}),
						newSingleLineTagParser("maxLength", &setMaxLength{paramValidations{&ps}, rxf(rxMaxLengthFmt, "")}),
						newSingleLineTagParser("pattern", &setPattern{paramValidations{&ps}, rxf(rxPatternFmt, "")}),
						newSingleLineTagParser("collectionFormat", &setCollectionFormat{paramValidations{&ps}, rxf(rxCollectionFormatFmt, "")}),
						newSingleLineTagParser("minItems", &setMinItems{paramValidations{&ps}, rxf(rxMinItemsFmt, "")}),
						newSingleLineTagParser("maxItems", &setMaxItems{paramValidations{&ps}, rxf(rxMaxItemsFmt, "")}),
						newSingleLineTagParser("unique", &setUnique{paramValidations{&ps}, rxf(rxUniqueFmt, "")}),
						newSingleLineTagParser("required", &setRequiredParam{&ps}),
						newSingleLineTagParser("in", &matchOnlyParam{&ps, rxIn}),
					}

					itemsTaggers := func(items *spec.Items, level int) []tagParser {
						// the expression is 1-index based not 0-index
						itemsPrefix := fmt.Sprintf(rxItemsPrefixFmt, level+1)

						return []tagParser{
							newSingleLineTagParser(fmt.Sprintf("items%dMaximum", level), &setMaximum{itemsValidations{items}, rxf(rxMaximumFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMinimum", level), &setMinimum{itemsValidations{items}, rxf(rxMinimumFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMultipleOf", level), &setMultipleOf{itemsValidations{items}, rxf(rxMultipleOfFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMinLength", level), &setMinLength{itemsValidations{items}, rxf(rxMinLengthFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMaxLength", level), &setMaxLength{itemsValidations{items}, rxf(rxMaxLengthFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dPattern", level), &setPattern{itemsValidations{items}, rxf(rxPatternFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dCollectionFormat", level), &setCollectionFormat{itemsValidations{items}, rxf(rxCollectionFormatFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMinItems", level), &setMinItems{itemsValidations{items}, rxf(rxMinItemsFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dMaxItems", level), &setMaxItems{itemsValidations{items}, rxf(rxMaxItemsFmt, itemsPrefix)}),
							newSingleLineTagParser(fmt.Sprintf("items%dUnique", level), &setUnique{itemsValidations{items}, rxf(rxUniqueFmt, itemsPrefix)}),
						}
					}

					// check if this is a primitive, if so parse the validations from the
					// doc comments of the slice declaration.
					if ftped, ok := fld.Type.(*ast.ArrayType); ok {
						ftpe := ftped
						items, level := ps.Items, 0
						for items != nil {
							switch iftpe := ftpe.Elt.(type) {
							case *ast.ArrayType:
								eleTaggers := itemsTaggers(items, level)
								sp.taggers = append(eleTaggers, sp.taggers...)
								ftpe = iftpe
							case *ast.Ident:
								if iftpe.Obj == nil {
									sp.taggers = append(itemsTaggers(items, level), sp.taggers...)
								}
								break
							default:
								return fmt.Errorf("unknown field type ele for %q", nm)
							}
							items = items.Items
							level = level + 1
						}
					}

				} else {

					sp.taggers = []tagParser{
						newSingleLineTagParser("required", &matchOnlyParam{&ps, rxRequired}),
						newSingleLineTagParser("in", &matchOnlyParam{&ps, rxIn}),
					}
				}
				if err := sp.Parse(fld.Doc); err != nil {
					return err
				}

				if ps.Name == "" {
					ps.Name = nm
				}

				if nm != gnm {
					ps.AddExtension("x-go-name", gnm)
				}
				pt[nm] = ps
			}
		}

		for k, p := range pt {
			for i, v := range operation.Parameters {
				if v.Name == k {
					operation.Parameters = append(operation.Parameters[:i], operation.Parameters[i+1:]...)
					break
				}
			}
			operation.Parameters = append(operation.Parameters, p)
		}
	}

	return nil
}
func (g *swaggerGenerator) generateSwaggerOperation(test IApiTest, defs spec.Definitions) (spec.Operation, error) {

	op := spec.Operation{}
	op.Responses = &spec.Responses{}
	op.Responses.StatusCodeResponses = map[int]spec.Response{}

	var description string
	processedQueryParams := map[string]interface{}{}
	processedPathParams := map[string]interface{}{}
	processedHeaderParams := map[string]interface{}{}
	for _, testCase := range test.TestCases() {
		// parameter definitions are collected from 2xx tests only
		if testCase.ExpectedHttpCode >= 200 && testCase.ExpectedHttpCode < 300 {
			description = testCase.Description

			for key, param := range testCase.Headers {
				if _, ok := processedHeaderParams[key]; ok {
					continue
				}

				specParam, err := generateSwaggerSpecParam(key, param, "header")
				if err != nil {
					return op, err
				}

				processedHeaderParams[key] = nil
				op.Parameters = append(op.Parameters, specParam)
			}

			for key, param := range testCase.PathParams {
				if _, ok := processedPathParams[key]; ok {
					continue
				}
				param.Required = true // path parameters are always required
				specParam, err := generateSwaggerSpecParam(key, param, "path")
				if err != nil {
					return op, err
				}

				processedPathParams[key] = nil
				op.Parameters = append(op.Parameters, specParam)
			}

			for key, param := range testCase.QueryParams {

				if _, ok := processedQueryParams[key]; ok {
					continue
				}

				specParam, err := generateSwaggerSpecParam(key, param, "query")
				if err != nil {
					return op, err
				}

				processedQueryParams[key] = nil
				op.Parameters = append(op.Parameters, specParam)
			}

			if testCase.RequestBody != nil {
				specParam := spec.Parameter{}
				specParam.Name = "body"
				specParam.In = "body"
				specParam.Required = true

				// TODO: right now it supports json, but should support marshaller depending on MIME type
				if content, err := json.MarshalIndent(testCase.RequestBody, "", "  "); err == nil {
					specParam.Description = string(content)
				}

				specParam.Schema = generateSpecSchema(testCase.RequestBody, defs)
				op.Parameters = append(op.Parameters, specParam)
			}
		}

		response := spec.Response{}
		response.Description = testCase.Description
		if testCase.ExpectedData != nil {
			response.Schema = generateSpecSchema(testCase.ExpectedData, defs)
			response.Examples = map[string]interface{}{
				"application/json": testCase.ExpectedData,
			}
		}

		op.Responses.StatusCodeResponses[testCase.ExpectedHttpCode] = response
	}

	op.Summary = description
	if taggable, ok := test.(ITaggable); ok {
		op.Tags = []string{taggable.Tag()}
	}

	return op, nil
}
Beispiel #4
0
func opSecurityDefsSetter(op *spec.Operation) func([]map[string][]string) {
	return func(securityDefs []map[string][]string) { op.Security = securityDefs }
}
Beispiel #5
0
func opSchemeSetter(op *spec.Operation) func([]string) {
	return func(schemes []string) { op.Schemes = schemes }
}
Beispiel #6
0
func opProducesSetter(op *spec.Operation) func([]string) {
	return func(produces []string) { op.Produces = produces }
}
Beispiel #7
0
func opConsumesSetter(op *spec.Operation) func([]string) {
	return func(consumes []string) { op.Consumes = consumes }
}