func (a *appGenerator) generateMain(app *GenApp) error { buf := bytes.NewBuffer(nil) if err := mainTemplate.Execute(buf, app); err != nil { return err } log.Println("rendered main template:", "server."+swag.ToGoName(app.Name)) return writeToFile(filepath.Join(a.Target, "cmd", swag.ToCommandName(swag.ToGoName(app.Name)+"Server")), "main", buf.Bytes()) }
func (a *appGenerator) generateAPIBuilder(app *GenApp) error { buf := bytes.NewBuffer(nil) if err := builderTemplate.Execute(buf, app); err != nil { return err } log.Println("rendered builder template:", app.Package+"."+swag.ToGoName(app.Name)) return writeToFile(filepath.Join(a.Target, a.ServerPackage, app.Package), swag.ToGoName(app.Name)+"Api", buf.Bytes()) }
func (a *appGenerator) generateConfigureAPI(app *GenApp) error { pth := filepath.Join(a.Target, "cmd", swag.ToCommandName(swag.ToGoName(app.Name)+"Server")) nm := "Configure" + swag.ToGoName(app.Name) if fileExists(pth, nm) { log.Println("skipped (already exists) configure api template:", app.Package+".Configure"+swag.ToGoName(app.Name)) return nil } buf := bytes.NewBuffer(nil) if err := configureAPITemplate.Execute(buf, app); err != nil { return err } log.Println("rendered configure api template:", app.Package+".Configure"+swag.ToGoName(app.Name)) return writeToFileIfNotExist(pth, nm, buf.Bytes()) }
func TestSchemaValidation_RequiredProps(t *testing.T) { specDoc, err := spec.Load("../fixtures/codegen/todolist.schemavalidation.yml") if assert.NoError(t, err) { k := "RequiredProps" schema := specDoc.Spec().Definitions[k] gm, err := makeGenDefinition(k, "models", schema, specDoc) if assert.NoError(t, err) { assert.Len(t, gm.Properties, 6) for _, p := range gm.Properties { if assert.True(t, p.Required) { buf := bytes.NewBuffer(nil) err := modelTemplate.Execute(buf, gm) if assert.NoError(t, err) { formatted, err := formatGoFile("required_props.go", buf.Bytes()) if assert.NoError(t, err) { res := string(formatted) assertInCode(t, k+") Validate(formats", res) assertInCode(t, "validate"+swag.ToGoName(p.Name), res) assertInCode(t, "err := validate.Required", res) assertInCode(t, "errors.CompositeValidationError(res...)", res) } } } } } } }
func (sg *schemaGenContext) NewAdditionalItems(schema *spec.Schema) *schemaGenContext { pg := sg.shallowClone() indexVar := pg.IndexVar pg.Name = sg.Name + " items" itemsLen := 0 if sg.Schema.Items != nil { itemsLen = sg.Schema.Items.Len() } var mod string if itemsLen > 0 { mod = "+" + strconv.Itoa(itemsLen) } if pg.Path == "" { pg.Path = "strconv.Itoa(" + indexVar + mod + ")" } else { pg.Path = pg.Path + "+ \".\" + strconv.Itoa(" + indexVar + mod + ")" } pg.IndexVar = indexVar pg.ValueExpr = sg.ValueExpr + "." + swag.ToGoName(sg.Name) + "Items[" + indexVar + "]" pg.Schema = spec.Schema{} if schema != nil { pg.Schema = *schema } pg.Required = false return pg }
func (t *typeResolver) resolveSchemaRef(schema *spec.Schema) (returns bool, result resolvedType, err error) { if schema.Ref.GetURL() != nil { returns = true ref, er := spec.ResolveRef(t.Doc.Spec(), &schema.Ref) if er != nil { err = er return } var nm = filepath.Base(schema.Ref.GetURL().Fragment) var tn string if gn, ok := ref.Extensions["x-go-name"]; ok { tn = gn.(string) } else { tn = swag.ToGoName(nm) } res, er := t.ResolveSchema(ref, false) if er != nil { err = er return } result = res result.GoType = tn if t.ModelsPackage != "" { result.GoType = t.ModelsPackage + "." + tn } return } return }
func modelValidations(gs GenSchema) commonValidations { return commonValidations{ propertyDescriptor: propertyDescriptor{ PropertyName: swag.ToGoName(gs.Name), ParamName: gs.Name, ValueExpression: gs.ValueExpression, IndexVar: gs.IndexVar, Path: gs.Path, IsContainer: gs.IsArray, IsPrimitive: gs.IsPrimitive, IsCustomFormatter: gs.IsCustomFormatter, IsMap: gs.IsMap, }, sharedValidations: sharedValidations{ Required: gs.Required, Maximum: gs.Maximum, ExclusiveMaximum: gs.ExclusiveMaximum, Minimum: gs.Minimum, ExclusiveMinimum: gs.ExclusiveMinimum, MaxLength: gs.MaxLength, MinLength: gs.MinLength, Pattern: gs.Pattern, MaxItems: gs.MaxItems, MinItems: gs.MinItems, UniqueItems: gs.UniqueItems, MultipleOf: gs.MultipleOf, Enum: gs.Enum, }, Type: gs.GoType, Format: gs.SwaggerFormat, //Default: model.Default, } }
func (sg *schemaGenContext) buildItems() error { presentsAsSingle := sg.Schema.Items != nil && sg.Schema.Items.Schema != nil if presentsAsSingle && sg.Schema.AdditionalItems != nil { // unsure if htis a valid of invalid schema return fmt.Errorf("single schema (%s) can't have additional items", sg.Name) } if presentsAsSingle { return sg.buildArray() } if sg.Schema.Items == nil { return nil } // This is a tuple, build a new model that represents this if sg.Named { sg.GenSchema.Name = sg.Name sg.GenSchema.GoType = swag.ToGoName(sg.Name) if sg.TypeResolver.ModelsPackage != "" { sg.GenSchema.GoType = sg.TypeResolver.ModelsPackage + "." + sg.GenSchema.GoType } for i, s := range sg.Schema.Items.Schemas { elProp := sg.NewTupleElement(&s, i) if err := elProp.makeGenSchema(); err != nil { return err } sg.MergeResult(elProp) elProp.GenSchema.Name = "p" + strconv.Itoa(i) sg.GenSchema.Properties = append(sg.GenSchema.Properties, elProp.GenSchema) } return nil } // for an anonoymous object, first build the new object // and then replace the current one with a $ref to the // new tuple object var sch spec.Schema sch.Typed("object", "") sch.Properties = make(map[string]spec.Schema) for i, v := range sg.Schema.Items.Schemas { sch.Required = append(sch.Required, "P"+strconv.Itoa(i)) sch.Properties["P"+strconv.Itoa(i)] = v } sch.AdditionalItems = sg.Schema.AdditionalItems tup := sg.makeNewStruct(sg.GenSchema.Name+"Tuple"+strconv.Itoa(sg.Index), sch) if err := tup.makeGenSchema(); err != nil { return err } tup.GenSchema.IsTuple = true tup.GenSchema.IsComplexObject = false tup.GenSchema.Title = tup.GenSchema.Name + " a representation of an anonymous Tuple type" tup.GenSchema.Description = "" sg.ExtraSchemas[tup.Name] = tup.GenSchema sg.Schema = *spec.RefProperty("#/definitions/" + tup.Name) if err := sg.makeGenSchema(); err != nil { return err } sg.MergeResult(tup) return nil }
func appNameOrDefault(specDoc *spec.Document, name, defaultName string) string { if name == "" { if specDoc.Spec().Info != nil && specDoc.Spec().Info.Title != "" { name = specDoc.Spec().Info.Title } else { name = defaultName } } return swag.ToGoName(name) }
func (b *codeGenOpBuilder) MakeResponse(receiver, name string, isSuccess bool, resolver *typeResolver, resp spec.Response) (GenResponse, error) { res := GenResponse{ Package: b.APIPackage, ReceiverName: receiver, Name: name, Description: resp.Description, DefaultImports: nil, Imports: nil, IsSuccess: isSuccess, } for hName, header := range resp.Headers { res.Headers = append(res.Headers, b.MakeHeader(receiver, hName, header)) } if resp.Schema != nil { sc := schemaGenContext{ Path: fmt.Sprintf("%q", name), Name: name + "Body", Receiver: receiver, ValueExpr: receiver, IndexVar: "i", Schema: *resp.Schema, Required: true, TypeResolver: resolver, Named: false, ExtraSchemas: make(map[string]GenSchema), } if err := sc.makeGenSchema(); err != nil { return GenResponse{}, err } for k, v := range sc.ExtraSchemas { b.ExtraSchemas[k] = v } schema := sc.GenSchema if schema.IsAnonymous { schema.Name = swag.ToGoName(sc.Name + " Body") nm := schema.Name if b.ExtraSchemas == nil { b.ExtraSchemas = make(map[string]GenSchema) } b.ExtraSchemas[schema.Name] = schema schema = GenSchema{} schema.IsAnonymous = false schema.GoType = nm schema.SwaggerType = nm } res.Schema = &schema } return res, nil }
func (sg *schemaGenContext) makeNewStruct(name string, schema spec.Schema) *schemaGenContext { sp := sg.TypeResolver.Doc.Spec() name = swag.ToGoName(name) if sg.TypeResolver.ModelName != sg.Name { name = swag.ToGoName(sg.TypeResolver.ModelName + " " + name) } sp.Definitions[name] = schema pg := schemaGenContext{ Path: "", Name: name, Receiver: "m", IndexVar: "i", ValueExpr: "m", Schema: schema, Required: false, TypeResolver: sg.TypeResolver, Named: true, ExtraSchemas: make(map[string]GenSchema), } pg.GenSchema.IsVirtual = true sg.ExtraSchemas[name] = pg.GenSchema return &pg }
func (sg *schemaGenContext) NewStructBranch(name string, schema spec.Schema) *schemaGenContext { pg := sg.shallowClone() if sg.Path == "" { pg.Path = fmt.Sprintf("%q", name) } else { pg.Path = pg.Path + "+\".\"+" + fmt.Sprintf("%q", name) } pg.Name = name pg.ValueExpr = pg.ValueExpr + "." + swag.ToGoName(name) pg.Schema = schema for _, fn := range sg.Schema.Required { if name == fn { pg.Required = true break } } return pg }
func paramValidations(receiver string, param spec.Parameter) commonValidations { accessor := swag.ToGoName(param.Name) paramName := swag.ToJSONName(param.Name) tpe := typeForParameter(param) _, isPrimitive := primitives[tpe] _, isCustomFormatter := customFormatters[tpe] return commonValidations{ propertyDescriptor: propertyDescriptor{ PropertyName: accessor, ParamName: paramName, ValueExpression: fmt.Sprintf("%s.%s", receiver, accessor), IndexVar: "i", Path: "\"" + paramName + "\"", IsContainer: param.Items != nil || tpe == "array", IsPrimitive: isPrimitive, IsCustomFormatter: isCustomFormatter, IsMap: strings.HasPrefix(tpe, "map"), }, sharedValidations: sharedValidations{ Required: param.Required, Maximum: param.Maximum, ExclusiveMaximum: param.ExclusiveMaximum, Minimum: param.Minimum, ExclusiveMinimum: param.ExclusiveMinimum, MaxLength: param.MaxLength, MinLength: param.MinLength, Pattern: param.Pattern, MaxItems: param.MaxItems, MinItems: param.MinItems, UniqueItems: param.UniqueItems, MultipleOf: param.MultipleOf, Enum: param.Enum, }, Type: tpe, Format: param.Format, Items: param.Items, Default: param.Default, } }
func (sg *schemaGenContext) buildProperties() error { for k, v := range sg.Schema.Properties { // check if this requires de-anonymizing, if so lift this as a new struct and extra schema tpe, err := sg.TypeResolver.ResolveSchema(&v, true) if err != nil { return err } vv := v var hasValidations bool if tpe.IsComplexObject && tpe.IsAnonymous && len(v.Properties) > 0 { pg := sg.makeNewStruct(sg.Name+swag.ToGoName(k), v) if sg.Path != "" { pg.Path = sg.Path + "+ \".\"+" + fmt.Sprintf("%q", k) } else { pg.Path = fmt.Sprintf("%q", k) } if err := pg.makeGenSchema(); err != nil { return err } vv = *spec.RefProperty("#/definitions/" + pg.Name) hasValidations = pg.GenSchema.HasValidations sg.MergeResult(pg) sg.ExtraSchemas[pg.Name] = pg.GenSchema } emprop := sg.NewStructBranch(k, vv) if err := emprop.makeGenSchema(); err != nil { return err } if hasValidations || emprop.GenSchema.HasValidations { emprop.GenSchema.HasValidations = true } sg.MergeResult(emprop) sg.GenSchema.Properties = append(sg.GenSchema.Properties, emprop.GenSchema) } sort.Sort(sg.GenSchema.Properties) return nil }
func (o *operationGenerator) Generate() error { // Build a list of codegen operations based on the tags, // the tag decides the actual package for an operation // the user specified package serves as root for generating the directory structure var operations []GenOperation authed := len(o.SecurityRequirements) > 0 var bldr codeGenOpBuilder bldr.Name = o.Name bldr.ModelsPackage = o.ModelsPackage bldr.Principal = o.Principal bldr.Target = o.Target bldr.Operation = o.Operation bldr.Authed = authed bldr.Doc = o.Doc //bldr.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(o.Target), o.ModelsPackage))} for _, tag := range o.Operation.Tags { if len(o.Tags) == 0 { bldr.APIPackage = tag op, err := bldr.MakeOperation() if err != nil { return err } operations = append(operations, op) continue } for _, ft := range o.Tags { if ft == tag { bldr.APIPackage = tag op, err := bldr.MakeOperation() if err != nil { return err } operations = append(operations, op) break } } } if len(operations) == 0 { bldr.APIPackage = o.APIPackage op, err := bldr.MakeOperation() if err != nil { return err } operations = append(operations, op) } for _, op := range operations { if o.DumpData { bb, _ := json.MarshalIndent(swag.ToDynamicJSON(op), "", " ") fmt.Fprintln(os.Stdout, string(bb)) continue } o.data = op o.pkg = op.Package o.cname = swag.ToGoName(op.Name) if o.IncludeHandler { if err := o.generateHandler(); err != nil { return fmt.Errorf("handler: %s", err) } log.Println("generated handler", op.Package+"."+o.cname) } if o.IncludeParameters && len(o.Operation.Parameters) > 0 { if err := o.generateParameterModel(); err != nil { return fmt.Errorf("parameters: %s", err) } log.Println("generated parameters", op.Package+"."+o.cname+"Parameters") } if len(o.Operation.Parameters) == 0 { log.Println("no parameters for operation", op.Package+"."+o.cname) } } return nil }
func (ctx *paramTestContext) assertGenParam(t testing.TB, param spec.Parameter, gp GenParameter) bool { // went with the verbose option here, easier to debug if !assert.Equal(t, param.In, gp.Location) { return false } if !assert.Equal(t, param.Name, gp.Name) { return false } if !assert.Equal(t, fmt.Sprintf("%q", param.Name), gp.Path) { return false } if !assert.Equal(t, "i", gp.IndexVar) { return false } if !assert.Equal(t, "a", gp.ReceiverName) { return false } if !assert.Equal(t, "a."+swag.ToGoName(param.Name), gp.ValueExpression) { return false } if !assert.Equal(t, ctx.Formatter, gp.Formatter) { return false } if !assert.Equal(t, ctx.Converter, gp.Converter) { return false } if !assert.Equal(t, param.Description, gp.Description) { return false } if !assert.Equal(t, param.CollectionFormat, gp.CollectionFormat) { return false } if !assert.Equal(t, param.Required, gp.Required) { return false } if !assert.Equal(t, param.Minimum, gp.Minimum) || !assert.Equal(t, param.ExclusiveMinimum, gp.ExclusiveMinimum) { return false } if !assert.Equal(t, param.Maximum, gp.Maximum) || !assert.Equal(t, param.ExclusiveMaximum, gp.ExclusiveMaximum) { return false } if !assert.Equal(t, param.MinLength, gp.MinLength) { return false } if !assert.Equal(t, param.MaxLength, gp.MaxLength) { return false } if !assert.Equal(t, param.Pattern, gp.Pattern) { return false } if !assert.Equal(t, param.MaxItems, gp.MaxItems) { return false } if !assert.Equal(t, param.MinItems, gp.MinItems) { return false } if !assert.Equal(t, param.UniqueItems, gp.UniqueItems) { return false } if !assert.Equal(t, param.MultipleOf, gp.MultipleOf) { return false } if !assert.EqualValues(t, param.Enum, gp.Enum) { return false } if !assert.Equal(t, param.Type, gp.SwaggerType) { return false } if !assert.Equal(t, param.Format, gp.SwaggerFormat) { return false } // verify rendered template if param.In == "body" { if !assertBodyParam(t, param, gp) { return false } return true } if ctx.Items != nil { return ctx.Items.Assert(t, param.Items, gp.Child) } return true }
func (b *codeGenOpBuilder) MakeParameter(receiver string, resolver *typeResolver, param spec.Parameter) (GenParameter, error) { var child *GenItems res := GenParameter{ Name: param.Name, Path: fmt.Sprintf("%q", param.Name), ValueExpression: fmt.Sprintf("%s.%s", receiver, swag.ToGoName(param.Name)), IndexVar: "i", BodyParam: nil, Default: param.Default, Enum: param.Enum, Description: param.Description, ReceiverName: receiver, CollectionFormat: param.CollectionFormat, Child: child, Location: param.In, } if param.In == "body" { sc := schemaGenContext{ Path: res.Path, Name: res.Name, Receiver: res.ReceiverName, ValueExpr: res.ReceiverName, IndexVar: res.IndexVar, Schema: *param.Schema, Required: param.Required, TypeResolver: resolver, Named: false, ExtraSchemas: make(map[string]GenSchema), } if err := sc.makeGenSchema(); err != nil { return GenParameter{}, err } schema := sc.GenSchema if schema.IsAnonymous { schema.Name = swag.ToGoName(b.Operation.ID + " Body") nm := schema.Name schema.GoType = nm schema.IsAnonymous = false if b.ExtraSchemas == nil { b.ExtraSchemas = make(map[string]GenSchema) } b.ExtraSchemas[nm] = schema schema = GenSchema{} schema.IsAnonymous = false schema.GoType = nm schema.SwaggerType = nm schema.IsComplexObject = true } res.Schema = &schema res.resolvedType = schema.resolvedType res.sharedValidations = schema.sharedValidations } else { res.resolvedType = simpleResolvedType(param.Type, param.Format, param.Items) res.sharedValidations = sharedValidations{ Required: param.Required, Maximum: param.Maximum, ExclusiveMaximum: param.ExclusiveMaximum, Minimum: param.Minimum, ExclusiveMinimum: param.ExclusiveMinimum, MaxLength: param.MaxLength, MinLength: param.MinLength, Pattern: param.Pattern, MaxItems: param.MaxItems, MinItems: param.MinItems, UniqueItems: param.UniqueItems, MultipleOf: param.MultipleOf, Enum: param.Enum, } if param.Items != nil { pi, err := b.MakeParameterItem(receiver, param.Name+" "+res.IndexVar, res.IndexVar+"i", "fmt.Sprintf(\"%s.%v\", "+res.Path+", "+res.IndexVar+")", res.ValueExpression+"["+res.IndexVar+"]", resolver, param.Items, nil) if err != nil { return GenParameter{}, err } res.Child = &pi } } hasNumberValidation := param.Maximum != nil || param.Minimum != nil || param.MultipleOf != nil hasStringValidation := param.MaxLength != nil || param.MinLength != nil || param.Pattern != "" hasSliceValidations := param.MaxItems != nil || param.MinItems != nil || param.UniqueItems hasValidations := hasNumberValidation || hasStringValidation || hasSliceValidations || len(param.Enum) > 0 res.Converter = stringConverters[res.GoType] res.Formatter = stringFormatters[res.GoType] res.HasValidations = hasValidations res.HasSliceValidations = hasSliceValidations return res, nil }
func fieldNameFromParam(param *Parameter) string { if nm, ok := param.Extensions.GetString("go-name"); ok { return nm } return swag.ToGoName(param.Name) }