func (m *Generator) generateToolSourceCode(pkg *codegen.Package) { file := pkg.CreateSourceFile("main.go") imports := append(m.Imports, codegen.SimpleImport("fmt"), codegen.SimpleImport("os"), codegen.SimpleImport("strings"), codegen.NewImport(".", "github.com/goadesign/goa/design"), codegen.NewImport(".", "github.com/goadesign/goa/design/dsl"), codegen.NewImport("_", filepath.ToSlash(codegen.DesignPackagePath)), ) file.WriteHeader("Code Generator", "main", imports) tmpl, err := template.New("generator").Parse(mainTmpl) if err != nil { panic(err) // bug } pkgName, err := codegen.PackageName(pkg.Abs()) if err != nil { panic(err) } context := map[string]string{ "Genfunc": m.Genfunc, "DesignPackage": codegen.DesignPackagePath, "PkgName": pkgName, } err = tmpl.Execute(file, context) if err != nil { panic(err) // bug } }
// generateMediaTypes iterates through the media types and generate the data structures and // marshaling code. func (g *Generator) generateMediaTypes() error { mtFile := filepath.Join(g.OutDir, "media_types.go") mtWr, err := NewMediaTypesWriter(mtFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Media Types", g.API.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("fmt"), codegen.SimpleImport("time"), codegen.SimpleImport("unicode/utf8"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } mtWr.WriteHeader(title, g.Target, imports) err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error { if mt.IsError() { return nil } if mt.Type.IsObject() || mt.Type.IsArray() { return mtWr.Execute(mt) } return nil }) g.genfiles = append(g.genfiles, mtFile) if err != nil { return err } return mtWr.FormatCode() }
// generateContexts iterates through the API resources and actions and generates the action // contexts. func (g *Generator) generateContexts() error { ctxFile := filepath.Join(g.OutDir, "contexts.go") ctxWr, err := NewContextsWriter(ctxFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Contexts", g.API.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("fmt"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("unicode/utf8"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } g.genfiles = append(g.genfiles, ctxFile) ctxWr.WriteHeader(title, g.Target, imports) err = g.API.IterateResources(func(r *design.ResourceDefinition) error { return r.IterateActions(func(a *design.ActionDefinition) error { ctxName := codegen.Goify(a.Name, true) + codegen.Goify(a.Parent.Name, true) + "Context" headers := r.Headers.Merge(a.Headers) if headers != nil && len(headers.Type.ToObject()) == 0 { headers = nil // So that {{if .Headers}} returns false in templates } params := a.AllParams() if params != nil && len(params.Type.ToObject()) == 0 { params = nil // So that {{if .Params}} returns false in templates } non101 := make(map[string]*design.ResponseDefinition) for k, v := range a.Responses { if v.Status != 101 { non101[k] = v } } ctxData := ContextTemplateData{ Name: ctxName, ResourceName: r.Name, ActionName: a.Name, Payload: a.Payload, Params: params, Headers: headers, Routes: a.Routes, Responses: non101, API: g.API, DefaultPkg: g.Target, Security: a.Security, } return ctxWr.Execute(&ctxData) }) }) if err != nil { return err } return ctxWr.FormatCode() }
// generateUserTypes iterates through the user types and generates the data structures and // marshaling code. func (g *Generator) generateUserTypes(outdir string, api *design.APIDefinition) error { var modelname, filename string err := GormaDesign.IterateStores(func(store *RelationalStoreDefinition) error { err := store.IterateModels(func(model *RelationalModelDefinition) error { modelname = strings.ToLower(codegen.Goify(model.ModelName, false)) filename = fmt.Sprintf("%s.go", modelname) utFile := filepath.Join(outdir, filename) err := os.RemoveAll(utFile) if err != nil { fmt.Println(err) } utWr, err := NewUserTypesWriter(utFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Models", api.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport(g.appPkgPath), codegen.SimpleImport("time"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/jinzhu/gorm"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("github.com/goadesign/goa/uuid"), } if model.Cached { imp := codegen.NewImport("cache", "github.com/patrickmn/go-cache") imports = append(imports, imp) imp = codegen.SimpleImport("strconv") imports = append(imports, imp) } utWr.WriteHeader(title, g.target, imports) data := &UserTypeTemplateData{ APIDefinition: api, UserType: model, DefaultPkg: g.target, AppPkg: g.appPkgPath, } err = utWr.Execute(data) g.genfiles = append(g.genfiles, utFile) if err != nil { fmt.Println(err) return err } err = utWr.FormatCode() if err != nil { fmt.Println(err) } return err }) return err }) return err }
// generateContexts iterates through the API resources and actions and generates the action // contexts. func (g *Generator) generateContexts(api *design.APIDefinition) error { ctxFile := filepath.Join(AppOutputDir(), "contexts.go") ctxWr, err := NewContextsWriter(ctxFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Contexts", api.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("fmt"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } ctxWr.WriteHeader(title, TargetPackage, imports) err = api.IterateResources(func(r *design.ResourceDefinition) error { return r.IterateActions(func(a *design.ActionDefinition) error { ctxName := codegen.Goify(a.Name, true) + codegen.Goify(a.Parent.Name, true) + "Context" headers := r.Headers.Merge(a.Headers) if headers != nil && len(headers.Type.ToObject()) == 0 { headers = nil // So that {{if .Headers}} returns false in templates } params := a.AllParams() if params != nil && len(params.Type.ToObject()) == 0 { params = nil // So that {{if .Params}} returns false in templates } ctxData := ContextTemplateData{ Name: ctxName, ResourceName: r.Name, ActionName: a.Name, Payload: a.Payload, Params: params, Headers: headers, Routes: a.Routes, Responses: BuildResponses(r.Responses, a.Responses), API: api, DefaultPkg: TargetPackage, Security: a.Security, } return ctxWr.Execute(&ctxData) }) }) g.genfiles = append(g.genfiles, ctxFile) if err != nil { return err } return ctxWr.FormatCode() }
func (g *Generator) generateCommands(commandsFile string, clientPkg string, funcs template.FuncMap, api *design.APIDefinition) error { file, err := codegen.SourceFileFor(commandsFile) if err != nil { return err } commandTypesTmpl := template.Must(template.New("commandTypes").Funcs(funcs).Parse(commandTypesTmpl)) commandsTmpl := template.Must(template.New("commands").Funcs(funcs).Parse(commandsTmpl)) imports := []*codegen.ImportSpec{ codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport(clientPkg), codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/spf13/cobra"), codegen.NewImport("log", "gopkg.in/inconshreveable/log15.v2"), } if err := file.WriteHeader("", "main", imports); err != nil { return err } g.genfiles = append(g.genfiles, commandsFile) file.Write([]byte("type (\n")) if err := api.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(action *design.ActionDefinition) error { return commandTypesTmpl.Execute(file, action) }) }); err != nil { return err } file.Write([]byte(")\n\n")) if err := api.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(action *design.ActionDefinition) error { data := map[string]interface{}{ "Action": action, "Resource": action.Parent, "Version": design.Design.APIVersionDefinition, } return commandsTmpl.Execute(file, data) }) }); err != nil { return err } return file.FormatCode() }
// generateMediaTypes iterates through the media types and generate the data structures and // marshaling code. func (g *Generator) generateMediaTypes(pkgDir string, funcs template.FuncMap) error { funcs["decodegotyperef"] = decodeGoTypeRef funcs["decodegotypename"] = decodeGoTypeName typeDecodeTmpl := template.Must(template.New("typeDecode").Funcs(funcs).Parse(typeDecodeTmpl)) mtFile := filepath.Join(pkgDir, "media_types.go") mtWr, err := genapp.NewMediaTypesWriter(mtFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Media Types", g.API.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("fmt"), codegen.SimpleImport("net/http"), codegen.SimpleImport("time"), codegen.SimpleImport("unicode/utf8"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } mtWr.WriteHeader(title, g.Target, imports) err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error { if (mt.Type.IsObject() || mt.Type.IsArray()) && !mt.IsError() { if err := mtWr.Execute(mt); err != nil { return err } } err := mt.IterateViews(func(view *design.ViewDefinition) error { p, _, err := mt.Project(view.Name) if err != nil { return err } if err := typeDecodeTmpl.Execute(mtWr.SourceFile, p); err != nil { return err } return nil }) return err }) g.genfiles = append(g.genfiles, mtFile) if err != nil { return err } return mtWr.FormatCode() }
func (g *Generator) generateClient(clientFile string, clientPkg string, funcs template.FuncMap, api *design.APIDefinition) error { file, err := codegen.SourceFileFor(clientFile) if err != nil { return err } clientTmpl := template.Must(template.New("client").Funcs(funcs).Parse(clientTmpl)) imports := []*codegen.ImportSpec{ codegen.SimpleImport("net/http"), codegen.NewImport("goaclient", "github.com/goadesign/goa/client"), } if err := file.WriteHeader("", "client", imports); err != nil { return err } g.genfiles = append(g.genfiles, clientFile) if err := clientTmpl.Execute(file, api); err != nil { return err } return file.FormatCode() }
// generateUserTypes iterates through the user types and generates the data structures and // marshaling code. func (g *Generator) generateUserTypes() error { utFile := filepath.Join(g.OutDir, "user_types.go") utWr, err := NewUserTypesWriter(utFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application User Types", g.API.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("fmt"), codegen.SimpleImport("time"), codegen.SimpleImport("unicode/utf8"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } utWr.WriteHeader(title, g.Target, imports) err = g.API.IterateUserTypes(func(t *design.UserTypeDefinition) error { return utWr.Execute(t) }) g.genfiles = append(g.genfiles, utFile) if err != nil { return err } return utWr.FormatCode() }
func (g *Generator) generateResourceClient(res *design.ResourceDefinition, funcs template.FuncMap) error { payloadTmpl := template.Must(template.New("payload").Funcs(funcs).Parse(payloadTmpl)) clientsTmpl := template.Must(template.New("clients").Funcs(funcs).Parse(clientsTmpl)) clientsWSTmpl := template.Must(template.New("clients").Funcs(funcs).Parse(clientsWSTmpl)) pathTmpl := template.Must(template.New("pathTemplate").Funcs(funcs).Parse(pathTmpl)) resFilename := codegen.SnakeCase(res.Name) if resFilename == typesFileName { // Avoid clash with datatypes.go resFilename += "_client" } filename := filepath.Join(codegen.OutputDir, resFilename+".go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("bytes"), codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("net/http"), codegen.SimpleImport("net/url"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } if err := file.WriteHeader("", "client", imports); err != nil { return err } g.genfiles = append(g.genfiles, filename) g.generatedTypes = make(map[string]bool) err = res.IterateActions(func(action *design.ActionDefinition) error { if action.Payload != nil { if err := payloadTmpl.Execute(file, action); err != nil { return err } g.generatedTypes[action.Payload.TypeName] = true } if action.Params != nil { params := make(design.Object, len(action.QueryParams.Type.ToObject())) for n, param := range action.QueryParams.Type.ToObject() { name := codegen.Goify(n, false) params[name] = param } action.QueryParams.Type = params } if action.Headers != nil { headers := make(design.Object, len(action.Headers.Type.ToObject())) for n, header := range action.Headers.Type.ToObject() { name := codegen.Goify(n, false) headers[name] = header } action.Headers.Type = headers } if action.WebSocket() { return clientsWSTmpl.Execute(file, action) } for i, r := range action.Routes { data := struct { Route *design.RouteDefinition Index int }{ Route: r, Index: i, } if err := pathTmpl.Execute(file, data); err != nil { return err } } return clientsTmpl.Execute(file, action) }) if err != nil { return err } return file.FormatCode() }
// Generate produces the skeleton main. func (g *Generator) Generate(api *design.APIDefinition) (_ []string, err error) { go utils.Catch(nil, func() { g.Cleanup() }) defer func() { if err != nil { g.Cleanup() } }() mainFile := filepath.Join(codegen.OutputDir, "main.go") if Force { os.Remove(mainFile) } g.genfiles = append(g.genfiles, mainFile) _, err = os.Stat(mainFile) funcs := template.FuncMap{ "tempvar": tempvar, "generateSwagger": generateSwagger, "okResp": okResp, "newControllerVersion": newControllerVersion, "targetPkg": func() string { return TargetPackage }, } if err != nil { file, err := codegen.SourceFileFor(mainFile) if err != nil { return nil, err } var outPkg string outPkg, err = codegen.PackagePath(codegen.OutputDir) if err != nil { return nil, err } outPkg = strings.TrimPrefix(filepath.ToSlash(outPkg), "src/") appPkg := path.Join(outPkg, "app") swaggerPkg := path.Join(outPkg, "swagger") imports := []*codegen.ImportSpec{ codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/goadesign/middleware"), codegen.SimpleImport(appPkg), codegen.SimpleImport(swaggerPkg), codegen.NewImport("log", "gopkg.in/inconshreveable/log15.v2"), } if generateSwagger() { jsonSchemaPkg := path.Join(outPkg, "schema") imports = append(imports, codegen.SimpleImport(jsonSchemaPkg)) } file.WriteHeader("", "main", imports) data := map[string]interface{}{ "Name": AppName, "API": api, } if err = file.ExecuteTemplate("main", mainT, funcs, data); err != nil { return nil, err } if err = file.FormatCode(); err != nil { return nil, err } } imp, err := codegen.PackagePath(codegen.OutputDir) if err != nil { return } imp = path.Join(filepath.ToSlash(imp), "app") imports := []*codegen.ImportSpec{codegen.SimpleImport(imp)} api.IterateVersions(func(v *design.APIVersionDefinition) error { if v.IsDefault() { return nil } imports = append(imports, codegen.SimpleImport(imp+"/"+codegen.VersionPackage(v.Version))) return nil }) err = api.IterateResources(func(r *design.ResourceDefinition) error { filename := filepath.Join(codegen.OutputDir, snakeCase(r.Name)+".go") if Force { if err := os.Remove(filename); err != nil { return err } } g.genfiles = append(g.genfiles, filename) if _, err := os.Stat(filename); err != nil { file, err := codegen.SourceFileFor(filename) if err != nil { return err } file.WriteHeader("", "main", imports) err = file.ExecuteTemplate("controller", ctrlT, funcs, r) if err != nil { return err } if err := file.FormatCode(); err != nil { return err } } return nil }) if err != nil { return } return g.genfiles, nil }
func (g *Generator) generateResourceClient(pkgDir string, res *design.ResourceDefinition, funcs template.FuncMap) error { payloadTmpl := template.Must(template.New("payload").Funcs(funcs).Parse(payloadTmpl)) pathTmpl := template.Must(template.New("pathTemplate").Funcs(funcs).Parse(pathTmpl)) resFilename := codegen.SnakeCase(res.Name) if resFilename == typesFileName { // Avoid clash with datatypes.go resFilename += "_client" } filename := filepath.Join(pkgDir, resFilename+".go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("bytes"), codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("io/ioutil"), codegen.SimpleImport("net/http"), codegen.SimpleImport("net/url"), codegen.SimpleImport("os"), codegen.SimpleImport("path"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } if err := file.WriteHeader("", g.Target, imports); err != nil { return err } g.genfiles = append(g.genfiles, filename) err = res.IterateFileServers(func(fs *design.FileServerDefinition) error { return g.generateFileServer(file, fs, funcs) }) err = res.IterateActions(func(action *design.ActionDefinition) error { if action.Payload != nil { found := false typeName := action.Payload.TypeName for _, t := range design.Design.Types { if t.TypeName == typeName { found = true break } } if !found { if err := payloadTmpl.Execute(file, action); err != nil { return err } } } for i, r := range action.Routes { data := struct { Route *design.RouteDefinition Index int }{ Route: r, Index: i, } if err := pathTmpl.Execute(file, data); err != nil { return err } } return g.generateActionClient(action, file, funcs) }) if err != nil { return err } return file.FormatCode() }
func (g *Generator) generateCommands(commandsFile string, clientPkg string, funcs template.FuncMap) error { file, err := codegen.SourceFileFor(commandsFile) if err != nil { return err } funcs["defaultRouteParams"] = defaultRouteParams funcs["defaultRouteTemplate"] = defaultRouteTemplate funcs["joinNames"] = joinNames funcs["joinRouteParams"] = joinRouteParams funcs["routes"] = routes funcs["flagType"] = flagType funcs["cmdFieldType"] = cmdFieldTypeString funcs["formatExample"] = formatExample funcs["shouldAddExample"] = shouldAddExample commandTypesTmpl := template.Must(template.New("commandTypes").Funcs(funcs).Parse(commandTypesTmpl)) commandsTmpl := template.Must(template.New("commands").Funcs(funcs).Parse(commandsTmpl)) commandsTmplWS := template.Must(template.New("commandsWS").Funcs(funcs).Parse(commandsTmplWS)) downloadCommandTmpl := template.Must(template.New("download").Funcs(funcs).Parse(downloadCommandTmpl)) registerTmpl := template.Must(template.New("register").Funcs(funcs).Parse(registerTmpl)) imports := []*codegen.ImportSpec{ codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("log"), codegen.SimpleImport("net/url"), codegen.SimpleImport("os"), codegen.SimpleImport("path"), codegen.SimpleImport("path/filepath"), codegen.SimpleImport("strings"), codegen.SimpleImport("strconv"), codegen.SimpleImport("time"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/spf13/cobra"), codegen.SimpleImport(clientPkg), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } if len(g.API.Resources) > 0 { imports = append(imports, codegen.NewImport("goaclient", "github.com/goadesign/goa/client")) } if err := file.WriteHeader("", "cli", imports); err != nil { return err } g.genfiles = append(g.genfiles, commandsFile) file.Write([]byte("type (\n")) var fs []*design.FileServerDefinition if err := g.API.IterateResources(func(res *design.ResourceDefinition) error { fs = append(fs, res.FileServers...) return res.IterateActions(func(action *design.ActionDefinition) error { return commandTypesTmpl.Execute(file, action) }) }); err != nil { return err } if len(fs) > 0 { file.Write([]byte(downloadCommandType)) } file.Write([]byte(")\n\n")) actions := make(map[string][]*design.ActionDefinition) hasDownloads := false g.API.IterateResources(func(res *design.ResourceDefinition) error { if len(res.FileServers) > 0 { hasDownloads = true } return res.IterateActions(func(action *design.ActionDefinition) error { name := codegen.Goify(action.Name, false) if as, ok := actions[name]; ok { actions[name] = append(as, action) } else { actions[name] = []*design.ActionDefinition{action} } return nil }) }) data := struct { Actions map[string][]*design.ActionDefinition Package string HasDownloads bool }{ Actions: actions, Package: g.Target, HasDownloads: hasDownloads, } if err := file.ExecuteTemplate("registerCmds", registerCmdsT, funcs, data); err != nil { return err } var fsdata []map[string]interface{} g.API.IterateResources(func(res *design.ResourceDefinition) error { if res.FileServers != nil { res.IterateFileServers(func(fs *design.FileServerDefinition) error { wcs := design.ExtractWildcards(fs.RequestPath) isDir := len(wcs) > 0 var reqDir, filename string if isDir { reqDir, _ = path.Split(fs.RequestPath) } else { _, filename = filepath.Split(fs.FilePath) } fsdata = append(fsdata, map[string]interface{}{ "IsDir": isDir, "RequestPath": fs.RequestPath, "FilePath": fs.FilePath, "FileName": filename, "Name": g.fileServerMethod(fs), "RequestDir": reqDir, }) return nil }) } return nil }) if fsdata != nil { data := struct { Package string FileServers []map[string]interface{} }{ Package: g.Target, FileServers: fsdata, } if err := downloadCommandTmpl.Execute(file, data); err != nil { return err } } err = g.API.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(action *design.ActionDefinition) error { data := map[string]interface{}{ "Action": action, "Resource": action.Parent, "Package": g.Target, "HasMultiContent": len(g.API.Consumes) > 1, } var err error if action.WebSocket() { err = commandsTmplWS.Execute(file, data) } else { err = commandsTmpl.Execute(file, data) } if err != nil { return err } err = registerTmpl.Execute(file, data) return err }) }) if err != nil { return err } return file.FormatCode() }
func (g *Generator) generateMain(mainFile string, clientPkg, cliPkg string, funcs template.FuncMap) error { imports := []*codegen.ImportSpec{ codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io/ioutil"), codegen.SimpleImport("net/http"), codegen.SimpleImport("os"), codegen.SimpleImport("time"), codegen.SimpleImport(clientPkg), codegen.SimpleImport(cliPkg), codegen.SimpleImport("github.com/spf13/cobra"), codegen.NewImport("goaclient", "github.com/goadesign/goa/client"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } funcs["defaultRouteParams"] = defaultRouteParams funcs["defaultRouteTemplate"] = defaultRouteTemplate funcs["joinNames"] = joinNames funcs["signerSignature"] = signerSignature funcs["signerArgs"] = signerArgs file, err := codegen.SourceFileFor(mainFile) if err != nil { return err } if err := file.WriteHeader("", "main", imports); err != nil { return err } g.genfiles = append(g.genfiles, mainFile) version := design.Design.Version if version == "" { version = "0" } hasSigners := false hasBasicAuthSigners := false hasAPIKeySigners := false hasTokenSigners := false for _, s := range g.API.SecuritySchemes { if signerType(s) != "" { hasSigners = true switch s.Type { case "basic": hasBasicAuthSigners = true case "apiKey": hasAPIKeySigners = true case "jwt", "oauth2": hasTokenSigners = true } } } data := struct { API *design.APIDefinition Version string Package string HasSigners bool HasBasicAuthSigners bool HasAPIKeySigners bool HasTokenSigners bool }{ API: g.API, Version: version, Package: g.Target, HasSigners: hasSigners, HasBasicAuthSigners: hasBasicAuthSigners, HasAPIKeySigners: hasAPIKeySigners, HasTokenSigners: hasTokenSigners, } if err := file.ExecuteTemplate("main", mainTmpl, funcs, data); err != nil { return err } return file.FormatCode() }
func (g *Generator) generateResourceTest() error { if len(g.API.Resources) == 0 { return nil } funcs := template.FuncMap{"isSlice": isSlice} testTmpl := template.Must(template.New("test").Funcs(funcs).Parse(testTmpl)) outDir, err := makeTestDir(g, g.API.Name) if err != nil { return err } appPkg, err := codegen.PackagePath(g.OutDir) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("bytes"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("log"), codegen.SimpleImport("net/http"), codegen.SimpleImport("net/http/httptest"), codegen.SimpleImport("net/url"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport(appPkg), codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/goadesign/goa/goatest"), codegen.SimpleImport("golang.org/x/net/context"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } return g.API.IterateResources(func(res *design.ResourceDefinition) error { filename := filepath.Join(outDir, codegen.SnakeCase(res.Name)+"_testing.go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } title := fmt.Sprintf("%s: %s TestHelpers", g.API.Context(), res.Name) if err := file.WriteHeader(title, "test", imports); err != nil { return err } var methods []*TestMethod if err := res.IterateActions(func(action *design.ActionDefinition) error { if err := action.IterateResponses(func(response *design.ResponseDefinition) error { if response.Status == 101 { // SwitchingProtocols, Don't currently handle WebSocket endpoints return nil } for routeIndex, route := range action.Routes { mediaType := design.Design.MediaTypeWithIdentifier(response.MediaType) if mediaType == nil { methods = append(methods, g.createTestMethod(res, action, response, route, routeIndex, nil, nil)) } else { if err := mediaType.IterateViews(func(view *design.ViewDefinition) error { methods = append(methods, g.createTestMethod(res, action, response, route, routeIndex, mediaType, view)) return nil }); err != nil { return err } } } return nil }); err != nil { return err } return nil }); err != nil { return err } g.genfiles = append(g.genfiles, filename) err = testTmpl.Execute(file, methods) if err != nil { panic(err) } return file.FormatCode() }) }
func (g *Generator) generateCommands(commandsFile string, clientPkg string, funcs template.FuncMap, api *design.APIDefinition) error { file, err := codegen.SourceFileFor(commandsFile) if err != nil { return err } commandTypesTmpl := template.Must(template.New("commandTypes").Funcs(funcs).Parse(commandTypesTmpl)) commandsTmpl := template.Must(template.New("commands").Funcs(funcs).Parse(commandsTmpl)) commandsTmplWS := template.Must(template.New("commandsWS").Funcs(funcs).Parse(commandsTmplWS)) registerTmpl := template.Must(template.New("register").Funcs(funcs).Parse(registerTmpl)) imports := []*codegen.ImportSpec{ codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("log"), codegen.SimpleImport("os"), codegen.SimpleImport("time"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("github.com/spf13/cobra"), codegen.SimpleImport(clientPkg), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), } if len(api.Resources) > 0 { imports = append(imports, codegen.NewImport("goaclient", "github.com/goadesign/goa/client")) } if err := file.WriteHeader("", "main", imports); err != nil { return err } g.genfiles = append(g.genfiles, commandsFile) file.Write([]byte("type (\n")) if err := api.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(action *design.ActionDefinition) error { return commandTypesTmpl.Execute(file, action) }) }); err != nil { return err } file.Write([]byte(")\n\n")) err = api.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(action *design.ActionDefinition) error { data := map[string]interface{}{ "Action": action, "Resource": action.Parent, } var err error if action.WebSocket() { err = commandsTmplWS.Execute(file, data) } else { err = commandsTmpl.Execute(file, data) } if err != nil { return err } err = registerTmpl.Execute(file, data) return err }) }) if err != nil { return err } return file.FormatCode() }
func (g *Generator) generateClientResources(clientPkg string, funcs template.FuncMap, api *design.APIDefinition) error { userTypeTmpl := template.Must(template.New("userType").Funcs(funcs).Parse(userTypeTmpl)) typeDecodeTmpl := template.Must(template.New("typeDecode").Funcs(funcs).Parse(typeDecodeTmpl)) err := api.IterateResources(func(res *design.ResourceDefinition) error { return g.generateResourceClient(res, funcs) }) if err != nil { return err } types := make(map[string]*design.UserTypeDefinition) for _, res := range api.Resources { for n, ut := range res.UserTypes() { types[n] = ut } } filename := filepath.Join(codegen.OutputDir, typesFileName+".go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("github.com/goadesign/goa"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("time"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } if err := file.WriteHeader("User Types", "client", imports); err != nil { return err } g.genfiles = append(g.genfiles, filename) // Generate user and media types used by action payloads and parameters err = api.IterateUserTypes(func(userType *design.UserTypeDefinition) error { if _, ok := g.generatedTypes[userType.TypeName]; ok { return nil } if _, ok := types[userType.TypeName]; ok { g.generatedTypes[userType.TypeName] = true return userTypeTmpl.Execute(file, userType) } return nil }) if err != nil { return err } // Generate media types used by action responses and their load helpers err = api.IterateResources(func(res *design.ResourceDefinition) error { return res.IterateActions(func(a *design.ActionDefinition) error { return a.IterateResponses(func(r *design.ResponseDefinition) error { if mt := api.MediaTypeWithIdentifier(r.MediaType); mt != nil { if _, ok := g.generatedTypes[mt.TypeName]; !ok { g.generatedTypes[mt.TypeName] = true if !mt.IsBuiltIn() { if err := userTypeTmpl.Execute(file, mt); err != nil { return err } } typeName := mt.TypeName if mt.IsBuiltIn() { elems := strings.Split(typeName, ".") typeName = elems[len(elems)-1] } if err := typeDecodeTmpl.Execute(file, mt); err != nil { return err } } } return nil }) }) }) if err != nil { return err } // Generate media types used in payloads but not in responses err = api.IterateMediaTypes(func(mediaType *design.MediaTypeDefinition) error { if mediaType.IsBuiltIn() { return nil } if _, ok := g.generatedTypes[mediaType.TypeName]; ok { return nil } if _, ok := types[mediaType.TypeName]; ok { g.generatedTypes[mediaType.TypeName] = true return userTypeTmpl.Execute(file, mediaType) } return nil }) if err != nil { return err } return file.FormatCode() }
func (g *Generator) generateClient(clientFile string, clientPkg string, funcs template.FuncMap) error { file, err := codegen.SourceFileFor(clientFile) if err != nil { return err } clientTmpl := template.Must(template.New("client").Funcs(funcs).Parse(clientTmpl)) // Compute list of encoders and decoders encoders, err := genapp.BuildEncoders(g.API.Produces, true) if err != nil { return err } decoders, err := genapp.BuildEncoders(g.API.Consumes, false) if err != nil { return err } im := make(map[string]bool) for _, data := range encoders { im[data.PackagePath] = true } for _, data := range decoders { im[data.PackagePath] = true } var packagePaths []string for packagePath := range im { if packagePath != "github.com/goadesign/goa" { packagePaths = append(packagePaths, packagePath) } } sort.Strings(packagePaths) // Setup codegen imports := []*codegen.ImportSpec{ codegen.SimpleImport("net/http"), codegen.SimpleImport("github.com/goadesign/goa"), codegen.NewImport("goaclient", "github.com/goadesign/goa/client"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } for _, packagePath := range packagePaths { imports = append(imports, codegen.SimpleImport(packagePath)) } if err := file.WriteHeader("", g.Target, imports); err != nil { return err } g.genfiles = append(g.genfiles, clientFile) // Generate data := struct { API *design.APIDefinition Encoders []*genapp.EncoderTemplateData Decoders []*genapp.EncoderTemplateData }{ API: g.API, Encoders: encoders, Decoders: decoders, } if err := clientTmpl.Execute(file, data); err != nil { return err } return file.FormatCode() }