Exemplo n.º 1
0
func (scp *schemaParser) parseAllOfMember(gofile *ast.File, schema *spec.Schema, expr ast.Expr, seenPreviously map[string]struct{}) error {
	// TODO: check if struct is annotated with swagger:model or known in the definitions otherwise
	var pkg *loader.PackageInfo
	var file *ast.File
	var gd *ast.GenDecl
	var ts *ast.TypeSpec
	var err error

	switch tpe := expr.(type) {
	case *ast.Ident:
		// do lookup of type
		// take primitives into account, they should result in an error for swagger
		pkg, err = scp.packageForFile(gofile)
		if err != nil {
			return err
		}
		file, gd, ts, err = findSourceFile(pkg, tpe.Name)
		if err != nil {
			return err
		}

	case *ast.SelectorExpr:
		// look up package, file and then type
		pkg, err = scp.packageForSelector(gofile, tpe.X)
		if err != nil {
			return fmt.Errorf("embedded struct: %v", err)
		}
		file, gd, ts, err = findSourceFile(pkg, tpe.Sel.Name)
		if err != nil {
			return fmt.Errorf("embedded struct: %v", err)
		}
	default:
		return fmt.Errorf("unable to resolve allOf member for: %v\n", expr)
	}

	sd := newSchemaDecl(file, gd, ts)
	if sd.hasAnnotation() {
		ref, err := spec.NewRef("#/definitions/" + sd.Name)
		if err != nil {
			return err
		}
		schema.Ref = ref
		scp.postDecls = append(scp.postDecls, *sd)
	} else {
		if st, ok := ts.Type.(*ast.StructType); ok {
			return scp.parseStructType(file, schema, st, seenPreviously)
		}
	}

	return nil
}
Exemplo n.º 2
0
func (scp *schemaParser) parseStructType(gofile *ast.File, bschema *spec.Schema, tpe *ast.StructType, seenPreviously map[string]struct{}) error {
	if tpe.Fields != nil {
		var schema *spec.Schema
		seenProperties := seenPreviously

		for _, fld := range tpe.Fields.List {
			if len(fld.Names) == 0 {
				// if this created an allOf property then we have to rejig the schema var
				// because all the fields collected that aren't from embedded structs should go in
				// their own proper schema
				// first process embedded structs in order of embedding
				if allOfMember(fld.Doc) {
					if schema == nil {
						schema = new(spec.Schema)
					}
					var newSch spec.Schema
					// 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 := scp.parseAllOfMember(gofile, &newSch, fld.Type, seenProperties); err != nil {
						return err
					}
					bschema.AllOf = append(bschema.AllOf, newSch)
					continue
				}
				if schema == nil {
					schema = bschema
				}

				// 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 := scp.parseEmbeddedStruct(gofile, schema, fld.Type, seenProperties); err != nil {
					return err
				}
			}
		}
		if schema != nil && len(bschema.AllOf) > 0 {
			bschema.AllOf = append(bschema.AllOf, *schema)
		}
		if schema == nil {
			schema = bschema
		}

		// then add and possibly override values
		if schema.Properties == nil {
			schema.Properties = make(map[string]spec.Schema)
		}
		schema.Typed("object", "")
		for _, fld := range tpe.Fields.List {
			if len(fld.Names) > 0 && fld.Names[0] != nil && fld.Names[0].IsExported() {
				var nm, gnm string
				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)
						if st.Get("json") != "" {
							nm = strings.Split(st.Get("json"), ",")[0]
						}
					}
				}

				ps := schema.Properties[nm]
				if err := parseProperty(scp, gofile, fld.Type, schemaTypable{&ps}); err != nil {
					return err
				}

				sp := new(sectionedParser)
				sp.setDescription = func(lines []string) { ps.Description = joinDropLast(lines) }
				if ps.Ref.GetURL() == nil {
					sp.taggers = []tagParser{
						newSingleLineTagParser("maximum", &setMaximum{schemaValidations{&ps}, rxf(rxMaximumFmt, "")}),
						newSingleLineTagParser("minimum", &setMinimum{schemaValidations{&ps}, rxf(rxMinimumFmt, "")}),
						newSingleLineTagParser("multipleOf", &setMultipleOf{schemaValidations{&ps}, rxf(rxMultipleOfFmt, "")}),
						newSingleLineTagParser("minLength", &setMinLength{schemaValidations{&ps}, rxf(rxMinLengthFmt, "")}),
						newSingleLineTagParser("maxLength", &setMaxLength{schemaValidations{&ps}, rxf(rxMaxLengthFmt, "")}),
						newSingleLineTagParser("pattern", &setPattern{schemaValidations{&ps}, rxf(rxPatternFmt, "")}),
						newSingleLineTagParser("minItems", &setMinItems{schemaValidations{&ps}, rxf(rxMinItemsFmt, "")}),
						newSingleLineTagParser("maxItems", &setMaxItems{schemaValidations{&ps}, rxf(rxMaxItemsFmt, "")}),
						newSingleLineTagParser("unique", &setUnique{schemaValidations{&ps}, rxf(rxUniqueFmt, "")}),
						newSingleLineTagParser("required", &setRequiredSchema{schema, nm}),
						newSingleLineTagParser("readOnly", &setReadOnlySchema{&ps}),
					}

					// check if this is a primitive, if so parse the validations from the
					// doc comments of the slice declaration.
					if ftpe, ok := fld.Type.(*ast.ArrayType); ok {
						if iftpe, ok := ftpe.Elt.(*ast.Ident); ok && iftpe.Obj == nil {
							if ps.Items != nil && ps.Items.Schema != nil {
								itemsTaggers := []tagParser{
									newSingleLineTagParser("itemsMaximum", &setMaximum{schemaValidations{ps.Items.Schema}, rxf(rxMaximumFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMinimum", &setMinimum{schemaValidations{ps.Items.Schema}, rxf(rxMinimumFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMultipleOf", &setMultipleOf{schemaValidations{ps.Items.Schema}, rxf(rxMultipleOfFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMinLength", &setMinLength{schemaValidations{ps.Items.Schema}, rxf(rxMinLengthFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMaxLength", &setMaxLength{schemaValidations{ps.Items.Schema}, rxf(rxMaxLengthFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsPattern", &setPattern{schemaValidations{ps.Items.Schema}, rxf(rxPatternFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMinItems", &setMinItems{schemaValidations{ps.Items.Schema}, rxf(rxMinItemsFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsMaxItems", &setMaxItems{schemaValidations{ps.Items.Schema}, rxf(rxMaxItemsFmt, rxItemsPrefix)}),
									newSingleLineTagParser("itemsUnique", &setUnique{schemaValidations{ps.Items.Schema}, rxf(rxUniqueFmt, rxItemsPrefix)}),
								}

								// items matchers should go before the default matchers so they match first
								sp.taggers = append(itemsTaggers, sp.taggers...)
							}
						}
					}
				} else {
					sp.taggers = []tagParser{
						newSingleLineTagParser("required", &setRequiredSchema{schema, nm}),
					}
				}
				if err := sp.Parse(fld.Doc); err != nil {
					return err
				}

				if nm != gnm {
					ps.AddExtension("x-go-name", gnm)
				}
				seenProperties[nm] = struct{}{}
				schema.Properties[nm] = ps
			}
		}
		for k := range schema.Properties {
			if _, ok := seenProperties[k]; !ok {
				delete(schema.Properties, k)
			}
		}
	}

	return nil
}