// GenerateClient generates a client library for a swagger spec document. func GenerateClient(name string, modelNames, operationIDs []string, opts GenOpts) error { // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } models := gatherModels(specDoc, modelNames) operations := gatherOperations(specDoc, operationIDs) defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } generator := appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), SpecDoc: specDoc, Models: models, Operations: operations, Target: opts.Target, DumpData: opts.DumpData, Package: mangleName(swag.ToFileName(opts.APIPackage), "api"), APIPackage: mangleName(swag.ToFileName(opts.APIPackage), "api"), ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: defaultScheme, } generator.Receiver = "o" return (&clientGenerator{generator}).Generate() }
// GenerateServerOperation generates a parameter model, parameter validator, http handler implementations for a given operation // It also generates an operation handler interface that uses the parameter model for handling a valid request. // Allows for specifying a list of tags to include only certain tags for the generation func GenerateServerOperation(operationNames, tags []string, includeHandler, includeParameters, includeResponses bool, opts GenOpts) error { if opts.TemplateDir != "" { if err := templates.LoadDir(opts.TemplateDir); err != nil { return err } } compileTemplates() // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } ops := gatherOperations(specDoc, operationNames) for operationName, opRef := range ops { method, path, operation := opRef.Method, opRef.Path, opRef.Op defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = "application/json" } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") serverPackage := mangleName(swag.ToFileName(opts.ServerPackage), "server") generator := operationGenerator{ Name: operationName, Method: method, Path: path, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), ServerPackage: serverPackage, Operation: *operation, SecurityRequirements: specDoc.SecurityRequirementsFor(operation), Principal: opts.Principal, Target: filepath.Join(opts.Target, serverPackage), Base: opts.Target, Tags: tags, IncludeHandler: includeHandler, IncludeParameters: includeParameters, IncludeResponses: includeResponses, DumpData: opts.DumpData, DefaultScheme: defaultScheme, DefaultProduces: defaultProduces, Doc: specDoc, } if err := generator.Generate(); err != nil { return err } } return nil }
func newAppGenerator(name string, modelNames, operationIDs []string, opts *GenOpts) (*appGenerator, error) { if opts.TemplateDir != "" { if err := templates.LoadDir(opts.TemplateDir); err != nil { return nil, err } } compileTemplates() // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return nil, err } models, err := gatherModels(specDoc, modelNames) if err != nil { return nil, err } operations := gatherOperations(specDoc, operationIDs) if len(operations) == 0 { return nil, errors.New("no operations were selected") } defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = "application/json" } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") return &appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Models: models, Operations: operations, Target: opts.Target, // Package: filepath.Base(opts.Target), DumpData: opts.DumpData, Package: apiPackage, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: defaultScheme, DefaultProduces: defaultProduces, GenOpts: opts, }, nil }
// GenerateServerOperation generates a parameter model, parameter validator, http handler implementations for a given operation // It also generates an operation handler interface that uses the parameter model for handling a valid request. // Allows for specifying a list of tags to include only certain tags for the generation func GenerateServerOperation(operationNames, tags []string, includeHandler, includeParameters, includeResponses bool, opts GenOpts) error { // Load the spec specPath, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } if len(operationNames) == 0 { operationNames = specDoc.OperationIDs() } for _, operationName := range operationNames { method, path, operation, ok := specDoc.OperationForName(operationName) if !ok { return fmt.Errorf("operation %q not found in %s", operationName, specPath) } defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") serverPackage := mangleName(swag.ToFileName(opts.ServerPackage), "server") generator := operationGenerator{ Name: operationName, Method: method, Path: path, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), ServerPackage: serverPackage, Operation: *operation, SecurityRequirements: specDoc.SecurityRequirementsFor(operation), Principal: opts.Principal, Target: filepath.Join(opts.Target, serverPackage), Base: opts.Target, Tags: tags, IncludeHandler: includeHandler, IncludeParameters: includeParameters, IncludeResponses: includeResponses, DumpData: opts.DumpData, DefaultScheme: defaultScheme, Doc: specDoc, } if err := generator.Generate(); err != nil { return err } } return nil }
func stripTestFromFileName(name string) string { ffn := swag.ToFileName(name) if strings.HasSuffix(ffn, "_test") { ffn = ffn[:len(ffn)-5] } return ffn }
func writeToFile(target, name string, content []byte) error { ffn := swag.ToFileName(name) + ".go" res, err := formatGoFile(ffn, content) if err != nil { log.Println(err) return writeFile(target, ffn, content) } return writeFile(target, ffn, res) }
// GenerateSupport generates the supporting files for an API func GenerateSupport(name string, modelNames, operationIDs []string, opts GenOpts) error { // Load the spec _, specDoc, err := loadSpec(opts.Spec) if err != nil { return err } models := gatherModels(specDoc, modelNames) operations := gatherOperations(specDoc, operationIDs) if len(operations) == 0 { return errors.New("no operations were selected") } defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = "http" } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") generator := appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Models: models, Operations: operations, Target: opts.Target, // Package: filepath.Base(opts.Target), DumpData: opts.DumpData, Package: apiPackage, APIPackage: apiPackage, ModelsPackage: mangleName(swag.ToFileName(opts.ModelPackage), "definitions"), ServerPackage: mangleName(swag.ToFileName(opts.ServerPackage), "server"), ClientPackage: mangleName(swag.ToFileName(opts.ClientPackage), "client"), Principal: opts.Principal, DefaultScheme: defaultScheme, } return generator.Generate() }
func writeToTestFile(target, name string, content []byte) error { ffn := swag.ToFileName(name) if !strings.HasSuffix(ffn, "_test") { ffn += "_test" } ffn += ".go" res, err := formatGoFile(filepath.Join(target, ffn), content) if err != nil { log.Println(err) return writeFile(target, ffn, content) } return writeFile(target, ffn, res) }
func mangleName(name, suffix string) string { if _, ok := reservedGoWordSet[swag.ToFileName(name)]; !ok { return name } return strings.Join([]string{name, suffix}, "_") }
func fileExists(target, name string) bool { ffn := swag.ToFileName(name) + ".go" _, err := os.Stat(filepath.Join(target, ffn)) return !os.IsNotExist(err) }
func (a *appGenerator) makeCodegenApp() (GenApp, error) { sw := a.SpecDoc.Spec() receiver := a.Receiver var defaultImports []string jsonb, _ := json.MarshalIndent(sw, "", " ") consumes, _ := a.makeConsumes() produces, _ := a.makeProduces() prin := a.Principal if prin == "" { prin = "interface{}" } security := a.makeSecuritySchemes() var genMods []GenDefinition importPath := filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ModelsPackage)) defaultImports = append(defaultImports, importPath) for mn, m := range a.Models { mod, err := makeGenDefinition( mn, a.ModelsPackage, m, a.SpecDoc, ) if err != nil { return GenApp{}, err } mod.ReceiverName = receiver genMods = append(genMods, *mod) } var genOps GenOperations tns := make(map[string]struct{}) var bldr codeGenOpBuilder bldr.ModelsPackage = a.ModelsPackage bldr.Principal = prin bldr.Target = a.Target bldr.DefaultImports = defaultImports bldr.Doc = a.SpecDoc for on, o := range a.Operations { bldr.Name = on bldr.Operation = o bldr.Authed = len(a.SpecDoc.SecurityRequirementsFor(&o)) > 0 ap := a.APIPackage if len(o.Tags) > 0 { for _, tag := range o.Tags { tns[tag] = struct{}{} bldr.APIPackage = swag.ToFileName(tag) op, err := bldr.MakeOperation() if err != nil { return GenApp{}, err } op.ReceiverName = receiver genOps = append(genOps, op) } } else { bldr.APIPackage = swag.ToFileName(ap) op, err := bldr.MakeOperation() if err != nil { return GenApp{}, err } op.ReceiverName = receiver genOps = append(genOps, op) } } for k := range tns { importPath := filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ServerPackage, a.APIPackage, k)) defaultImports = append(defaultImports, importPath) } sort.Sort(genOps) defaultConsumes := "application/json" rc := a.SpecDoc.RequiredConsumes() if len(rc) > 0 { defaultConsumes = rc[0] } defaultProduces := "application/json" rp := a.SpecDoc.RequiredProduces() if len(rp) > 0 { defaultProduces = rp[0] } return GenApp{ Package: a.Package, ReceiverName: receiver, Name: a.Name, Host: sw.Host, BasePath: sw.BasePath, Schemes: sw.Schemes, ExternalDocs: sw.ExternalDocs, Info: sw.Info, Consumes: consumes, Produces: produces, DefaultConsumes: defaultConsumes, DefaultProduces: defaultProduces, DefaultImports: defaultImports, SecurityDefinitions: security, Models: genMods, Operations: genOps, Principal: prin, SwaggerJSON: fmt.Sprintf("%#v", jsonb), }, 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 GenOperations authed := len(o.SecurityRequirements) > 0 var bldr codeGenOpBuilder bldr.Name = o.Name bldr.Method = o.Method bldr.Path = o.Path bldr.ModelsPackage = o.ModelsPackage bldr.Principal = o.Principal bldr.Target = o.Target bldr.Operation = o.Operation bldr.Authed = authed bldr.Doc = o.Doc bldr.DefaultScheme = o.DefaultScheme bldr.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(o.Base), o.ModelsPackage))} for _, tag := range o.Operation.Tags { if len(o.Tags) == 0 { bldr.APIPackage = mangleName(swag.ToFileName(tag), o.APIPackage) op, err := bldr.MakeOperation() if err != nil { return err } operations = append(operations, op) continue } for _, ft := range o.Tags { if ft == tag { bldr.APIPackage = mangleName(swag.ToFileName(tag), o.APIPackage) 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) } sort.Sort(operations) 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) } opParams := o.Doc.ParametersFor(o.Operation.ID) if o.IncludeParameters && len(opParams) > 0 { if err := o.generateParameterModel(); err != nil { return fmt.Errorf("parameters: %s", err) } log.Println("generated parameters", op.Package+"."+o.cname+"Parameters") } if o.IncludeResponses && len(op.Responses) > 0 { if err := o.generateResponses(); err != nil { return fmt.Errorf("responses: %s", err) } log.Println("generated responses", op.Package+"."+o.cname+"Responses") } if len(opParams) == 0 { log.Println("no parameters for operation", op.Package+"."+o.cname) } } 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 GenOperations authed := len(o.SecurityRequirements) > 0 var bldr codeGenOpBuilder bldr.Name = o.Name bldr.Method = o.Method bldr.Path = o.Path bldr.ModelsPackage = o.ModelsPackage bldr.Principal = o.Principal bldr.Target = o.Target bldr.Operation = o.Operation bldr.Authed = authed bldr.Doc = o.Doc bldr.DefaultScheme = o.DefaultScheme bldr.DefaultProduces = o.DefaultProduces bldr.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(o.Base), o.ModelsPackage))} bldr.RootAPIPackage = o.APIPackage for _, tag := range o.Operation.Tags { if len(o.Tags) == 0 { bldr.APIPackage = mangleName(swag.ToFileName(tag), o.APIPackage) op, err := bldr.MakeOperation() if err != nil { return err } operations = append(operations, op) continue } for _, ft := range o.Tags { if ft == tag { bldr.APIPackage = mangleName(swag.ToFileName(tag), o.APIPackage) 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) } sort.Sort(operations) for _, op := range operations { if o.DumpData { bb, _ := json.MarshalIndent(swag.ToDynamicJSON(op), "", " ") fmt.Fprintln(os.Stdout, string(bb)) continue } og := new(opGen) og.IncludeHandler = o.IncludeHandler og.IncludeParameters = o.IncludeParameters og.IncludeResponses = o.IncludeResponses og.data = &op og.pkg = op.Package og.cname = swag.ToGoName(op.Name) og.Doc = o.Doc og.Target = o.Target og.APIPackage = o.APIPackage return og.Generate() } return nil }
func (a *appGenerator) makeCodegenApp() (GenApp, error) { log.Println("building a plan for generation") sw := a.SpecDoc.Spec() receiver := a.Receiver var defaultImports []string jsonb, _ := json.MarshalIndent(sw, "", " ") consumes, _ := a.makeConsumes() produces, _ := a.makeProduces() prin := a.Principal if prin == "" { prin = "interface{}" } security := a.makeSecuritySchemes() var genMods []GenDefinition importPath := filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ModelsPackage)) defaultImports = append(defaultImports, importPath) log.Println("planning definitions") for mn, m := range a.Models { mod, err := makeGenDefinition( mn, a.ModelsPackage, m, a.SpecDoc, ) if err != nil { return GenApp{}, err } //mod.ReceiverName = receiver genMods = append(genMods, *mod) } log.Println("planning operations") tns := make(map[string]struct{}) var genOps GenOperations for on, opp := range a.Operations { o := opp.Op o.ID = on var bldr codeGenOpBuilder bldr.ModelsPackage = a.ModelsPackage bldr.Principal = prin bldr.Target = a.Target bldr.DefaultImports = defaultImports bldr.DefaultScheme = a.DefaultScheme bldr.Doc = &(*a.SpecDoc) // TODO: change operation name to something safe bldr.Name = on bldr.Operation = *o bldr.Method = opp.Method bldr.Path = opp.Path bldr.Authed = len(a.SpecDoc.SecurityRequirementsFor(o)) > 0 ap := a.APIPackage bldr.RootAPIPackage = swag.ToFileName(a.APIPackage) if len(o.Tags) > 0 { for _, tag := range o.Tags { tns[tag] = struct{}{} bldr.APIPackage = mangleName(swag.ToFileName(tag), a.APIPackage) op, err := bldr.MakeOperation() if err != nil { return GenApp{}, err } op.ReceiverName = receiver genOps = append(genOps, op) } } else { bldr.APIPackage = swag.ToFileName(ap) op, err := bldr.MakeOperation() if err != nil { return GenApp{}, err } op.ReceiverName = receiver genOps = append(genOps, op) } } for k := range tns { importPath := filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ServerPackage, a.APIPackage, swag.ToFileName(k))) defaultImports = append(defaultImports, importPath) } sort.Sort(genOps) log.Println("grouping operations into packages") opsGroupedByTag := make(map[string]GenOperations) for _, operation := range genOps { if operation.Package == "" { operation.Package = a.Package } opsGroupedByTag[operation.Package] = append(opsGroupedByTag[operation.Package], operation) } var opGroups GenOperationGroups for k, v := range opsGroupedByTag { sort.Sort(v) opGroup := GenOperationGroup{ Name: k, Operations: v, DefaultImports: []string{filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ModelsPackage))}, RootPackage: a.APIPackage, } opGroups = append(opGroups, opGroup) var importPath string if k == a.APIPackage { importPath = filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ServerPackage, a.APIPackage)) } else { importPath = filepath.ToSlash(filepath.Join(baseImport(a.Target), a.ServerPackage, a.APIPackage, k)) } defaultImports = append(defaultImports, importPath) } sort.Sort(opGroups) log.Println("planning meta data and facades") defaultConsumes := "application/json" rc := a.SpecDoc.RequiredConsumes() if len(rc) > 0 { defaultConsumes = rc[0] } defaultProduces := "application/json" rp := a.SpecDoc.RequiredProduces() if len(rp) > 0 { defaultProduces = rp[0] } var collectedSchemes []string var extraSchemes []string for _, op := range genOps { collectedSchemes = concatUnique(collectedSchemes, op.Schemes) extraSchemes = concatUnique(extraSchemes, op.ExtraSchemes) } host := "localhost" if sw.Host != "" { host = sw.Host } basePath := "/" if sw.BasePath != "" { basePath = "/" } return GenApp{ Package: a.Package, ReceiverName: receiver, Name: a.Name, Host: host, BasePath: basePath, Schemes: schemeOrDefault(collectedSchemes, a.DefaultScheme), ExtraSchemes: extraSchemes, ExternalDocs: sw.ExternalDocs, Info: sw.Info, Consumes: consumes, Produces: produces, DefaultConsumes: defaultConsumes, DefaultProduces: defaultProduces, DefaultImports: defaultImports, SecurityDefinitions: security, Models: genMods, Operations: genOps, OperationGroups: opGroups, Principal: prin, SwaggerJSON: fmt.Sprintf("%#v", jsonb), }, nil }