func stripTestFromFileName(name string) string { ffn := swag.ToFileName(name) if strings.HasSuffix(ffn, "_test") { ffn = ffn[:len(ffn)-5] } return ffn }
func testAppGenertor(t testing.TB, specPath, name string) (*appGenerator, error) { specDoc, err := loads.Spec(specPath) if !assert.NoError(t, err) { return nil, err } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, nil) if !assert.NoError(t, err) { return nil, err } operations := gatherOperations(analyzed, nil) if len(operations) == 0 { return nil, errors.New("no operations were selected") } opts := testGenOpts() apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") return &appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Analyzed: analyzed, Models: models, Operations: operations, 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: "http", DefaultProduces: runtime.JSONMime, DefaultConsumes: runtime.JSONMime, GenOpts: &opts, }, nil }
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) { 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, true, true, ) 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 bldr.Analyzed = a.Analyzed // 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.Analyzed.SecurityRequirementsFor(o)) > 0 ap := a.APIPackage bldr.RootAPIPackage = swag.ToFileName(a.APIPackage) bldr.WithContext = a.GenOpts != nil && a.GenOpts.WithContext 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, WithContext: a.GenOpts != nil && a.GenOpts.WithContext, } 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") 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 = sw.BasePath } return GenApp{ APIPackage: a.ServerPackage, 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: a.DefaultConsumes, DefaultProduces: a.DefaultProduces, DefaultImports: defaultImports, SecurityDefinitions: security, Models: genMods, Operations: genOps, OperationGroups: opGroups, Principal: prin, SwaggerJSON: fmt.Sprintf("%#v", jsonb), ExcludeSpec: a.GenOpts != nil && a.GenOpts.ExcludeSpec, WithContext: a.GenOpts != nil && a.GenOpts.WithContext, }, 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 } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, modelNames) if err != nil { return nil, err } operations := gatherOperations(analyzed, 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 = runtime.JSONMime } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } apiPackage := mangleName(swag.ToFileName(opts.APIPackage), "api") return &appGenerator{ Name: appNameOrDefault(specDoc, name, "swagger"), Receiver: "o", SpecDoc: specDoc, Analyzed: analyzed, 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, DefaultConsumes: defaultConsumes, 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 { 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 } analyzed := analysis.New(specDoc.Spec()) ops := gatherOperations(analyzed, operationNames) for operationName, opRef := range ops { method, path, operation := opRef.Method, opRef.Path, opRef.Op defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = sHTTP } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = runtime.JSONMime } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } 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: analyzed.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, DefaultConsumes: defaultConsumes, Doc: specDoc, Analyzed: analyzed, } if err := generator.Generate(); err != nil { return err } } 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.Analyzed = o.Analyzed bldr.DefaultScheme = o.DefaultScheme bldr.DefaultProduces = o.DefaultProduces bldr.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(o.Base), o.ModelsPackage))} bldr.RootAPIPackage = o.APIPackage bldr.WithContext = o.WithContext bldr.DefaultConsumes = o.DefaultConsumes 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.Analyzed = o.Analyzed og.Target = o.Target og.APIPackage = o.APIPackage og.WithContext = o.WithContext return og.Generate() } return nil }
// GenerateClient generates a client library for a swagger spec document. func GenerateClient(name string, modelNames, operationIDs []string, opts GenOpts) error { defer func() { typeMapping["binary"] = "io.ReadCloser" }() typeMapping["binary"] = "io.Writer" customFormatters["io.Writer"] = struct{}{} 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 } analyzed := analysis.New(specDoc.Spec()) models, err := gatherModels(specDoc, modelNames) if err != nil { return err } operations := gatherOperations(analyzed, operationIDs) defaultScheme := opts.DefaultScheme if defaultScheme == "" { defaultScheme = sHTTP } defaultConsumes := opts.DefaultConsumes if defaultConsumes == "" { defaultConsumes = runtime.JSONMime } defaultProduces := opts.DefaultProduces if defaultProduces == "" { defaultProduces = runtime.JSONMime } generator := appGenerator{ Name: appNameOrDefault(specDoc, name, "rest"), SpecDoc: specDoc, Analyzed: analyzed, Models: models, Operations: operations, Target: opts.Target, DumpData: opts.DumpData, Package: mangleName(swag.ToFileName(opts.ClientPackage), "client"), 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, DefaultProduces: defaultProduces, DefaultConsumes: defaultConsumes, GenOpts: &opts, } generator.Receiver = "o" return (&clientGenerator{generator}).Generate() }