func (g openAPITypeWriter) generateMapProperty(t *types.Type) error { keyType := resolveAliasAndPtrType(t.Key) elemType := resolveAliasAndPtrType(t.Elem) // According to OpenAPI examples, only map from string is supported if keyType.Name.Name != "string" { return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t) } g.Do("Type: []string{\"object\"},\n", nil) g.Do("AdditionalProperties: &spec.SchemaOrBool{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) typeString, format := common.GetOpenAPITypeFormat(elemType.String()) if typeString != "" { g.generateSimpleProperty(typeString, format) g.Do("},\n},\n},\n", nil) return nil } switch elemType.Kind { case types.Builtin: return fmt.Errorf("please add type %v to getOpenAPITypeFormat function.", elemType) case types.Struct: g.generateReferenceProperty(t.Elem) case types.Slice, types.Array: g.generateSliceProperty(elemType) default: return fmt.Errorf("map Element kind %v is not supported in %v", elemType.Kind, t.Name) } g.Do("},\n},\n},\n", nil) return nil }
func (g openAPITypeWriter) generate(t *types.Type) error { // Only generate for struct type and ignore the rest switch t.Kind { case types.Struct: args := argsFromType(t) g.Do("\"$.$\": ", typeShortName(t)) if hasOpenAPIDefinitionMethod(t) { g.Do("$.type|raw${}.OpenAPIDefinition(),", args) return nil } g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) g.generateDescription(t.CommentLines) g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args) required := []string{} for _, m := range t.Members { if hasOpenAPITagValue(m.CommentLines, tagValueFalse) { continue } name := getReferableName(&m) if name == "" { continue } if !hasOptionalTag(m.CommentLines) { required = append(required, name) } if err := g.generateProperty(&m); err != nil { return err } } g.Do("},\n", nil) if len(required) > 0 { g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\"")) } g.Do("},\n},\n", nil) g.Do("Dependencies: []string{\n", args) // Map order is undefined, sort them or we may get a different file generated each time. keys := []string{} for k := range g.refTypes { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { v := g.refTypes[k] if t, _ := common.GetOpenAPITypeFormat(v.String()); t != "" { // This is a known type, we do not need a reference to it // Will eliminate special case of time.Time continue } g.Do("\"$.$\",", k) } g.Do("},\n},\n", nil) } return nil }
func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error { elemType := resolveAliasAndPtrType(t.Elem) g.Do("Type: []string{\"array\"},\n", nil) g.Do("Items: &spec.SchemaOrArray{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) switch elemType.Kind { case types.Builtin: typeString, format := common.GetOpenAPITypeFormat(elemType.String()) g.generateSimpleProperty(typeString, format) case types.Struct: g.generateReferenceProperty(t.Elem) default: return fmt.Errorf("slice Element kind %v is not supported in %v", elemType.Kind, t) } g.Do("},\n},\n},\n", nil) return nil }
func (g openAPITypeWriter) generateProperty(m *types.Member) error { name := getReferableName(m) if name == "" { return nil } g.Do("\"$.$\": {\n", name) g.Do("SchemaProps: spec.SchemaProps{\n", nil) g.generateDescription(m.CommentLines) jsonTags := getJsonTags(m) if len(jsonTags) > 1 && jsonTags[1] == "string" { g.generateSimpleProperty("string", "") g.Do("},\n},\n", nil) return nil } t := resolveAliasAndPtrType(m.Type) // If we can get a openAPI type and format for this type, we consider it to be simple property typeString, format := common.GetOpenAPITypeFormat(t.String()) if typeString != "" { g.generateSimpleProperty(typeString, format) g.Do("},\n},\n", nil) return nil } switch t.Kind { case types.Builtin: return fmt.Errorf("please add type %v to getOpenAPITypeFormat function.", t) case types.Map: if err := g.generateMapProperty(t); err != nil { return err } case types.Slice, types.Array: if err := g.generateSliceProperty(t); err != nil { return err } case types.Struct, types.Interface: g.generateReferenceProperty(t) default: return fmt.Errorf("cannot generate spec for type %v.", t) } g.Do("},\n},\n", nil) return g.Error() }
func (o *openAPI) toSchema(typeName string, model interface{}) (_ *spec.Schema, err error) { if openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(typeName); openAPIType != "" { return &spec.Schema{ SchemaProps: spec.SchemaProps{ Type: []string{openAPIType}, Format: openAPIFormat, }, }, nil } else { ref := "#/definitions/" + typeName if model != nil { ref, err = o.buildDefinitionForType(model) if err != nil { return nil, err } } return &spec.Schema{ SchemaProps: spec.SchemaProps{ Ref: spec.MustCreateRef(ref), }, }, nil } }
func (o *openAPI) buildParameter(restParam restful.ParameterData) (ret spec.Parameter, err error) { ret = spec.Parameter{ ParamProps: spec.ParamProps{ Name: restParam.Name, Description: restParam.Description, Required: restParam.Required, }, } switch restParam.Kind { case restful.BodyParameterKind: ret.In = "body" ret.Schema, err = o.toSchema(restParam.DataType, nil) return ret, err case restful.PathParameterKind: ret.In = "path" if !restParam.Required { return ret, fmt.Errorf("path parameters should be marked at required for parameter %v", restParam) } case restful.QueryParameterKind: ret.In = "query" case restful.HeaderParameterKind: ret.In = "header" case restful.FormParameterKind: ret.In = "form" default: return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind) } openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(restParam.DataType) if openAPIType == "" { return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType) } ret.Type = openAPIType ret.Format = openAPIFormat ret.UniqueItems = !restParam.AllowMultiple return ret, nil }