// generateControllers iterates through the version resources and generates the low level // controllers. func (g *Generator) generateControllers(verdir string, version *design.APIVersionDefinition) error { ctlFile := filepath.Join(verdir, "controllers.go") ctlWr, err := NewControllersWriter(ctlFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Controllers", version.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("net/http"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("github.com/goadesign/goa"), } if !version.IsDefault() { appPkg, err := AppPackagePath() if err != nil { return err } imports = append(imports, codegen.SimpleImport(appPkg)) } encoderMap, err := BuildEncoderMap(version.Produces, true) if err != nil { return err } decoderMap, err := BuildEncoderMap(version.Consumes, false) if err != nil { return err } encoderImports := make(map[string]bool) for _, data := range encoderMap { encoderImports[data.PackagePath] = true } for _, data := range decoderMap { encoderImports[data.PackagePath] = true } for packagePath := range encoderImports { if !design.IsGoaEncoder(packagePath) { imports = append(imports, codegen.SimpleImport(packagePath)) } } ctlWr.WriteHeader(title, packageName(version), imports) var controllersData []*ControllerTemplateData version.IterateResources(func(r *design.ResourceDefinition) error { if !r.SupportsVersion(version.Version) { return nil } data := &ControllerTemplateData{API: design.Design, Resource: codegen.Goify(r.Name, true)} err := r.IterateActions(func(a *design.ActionDefinition) error { context := fmt.Sprintf("%s%sContext", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true)) unmarshal := fmt.Sprintf("unmarshal%s%sPayload", codegen.Goify(a.Name, true), codegen.Goify(r.Name, true)) action := map[string]interface{}{ "Name": codegen.Goify(a.Name, true), "Routes": a.Routes, "Context": context, "Unmarshal": unmarshal, "Payload": a.Payload, } data.Actions = append(data.Actions, action) return nil }) if err != nil { return err } if len(data.Actions) > 0 { data.EncoderMap = encoderMap data.DecoderMap = decoderMap data.Version = version controllersData = append(controllersData, data) } return nil }) g.genfiles = append(g.genfiles, ctlFile) if err = ctlWr.Execute(controllersData); err != nil { return err } return ctlWr.FormatCode() }
// BuildEncoderMap builds the template data needed to render the given encoding definitions. // This extra map is needed to handle the case where a single encoding definition maps to multiple // encoding packages. The data is indexed by encoder Go package path. func BuildEncoderMap(info []*design.EncodingDefinition, encoder bool) (map[string]*EncoderTemplateData, error) { if len(info) == 0 { return nil, nil } packages := make(map[string]map[string]bool) for _, enc := range info { supporting := enc.SupportingPackages() if supporting == nil { // shouldn't happen - DSL validation shouldn't allow it - be graceful continue } for ppath, mimeTypes := range supporting { if _, ok := packages[ppath]; !ok { packages[ppath] = make(map[string]bool) } for _, m := range mimeTypes { packages[ppath][m] = true } } } data := make(map[string]*EncoderTemplateData, len(packages)) if len(info[0].MIMETypes) == 0 { return nil, fmt.Errorf("No mime type associated with encoding info for package %s", info[0].PackagePath) } defaultMediaType := info[0].MIMETypes[0] for p, ms := range packages { pkgName := "goa" if !design.IsGoaEncoder(p) { srcPath, err := codegen.PackageSourcePath(p) if err == nil { pkgName, err = codegen.PackageName(srcPath) } if err != nil { return nil, fmt.Errorf("failed to load package %s (%s)", p, err) } } mimeTypes := make([]string, len(ms)) isDefault := false i := 0 for m := range ms { if m == defaultMediaType { isDefault = true } mimeTypes[i] = m i++ } first := mimeTypes[0] sort.Strings(mimeTypes) var factory string if encoder { if !design.IsGoaEncoder(p) { factory = "EncoderFactory" } else { factory = design.KnownEncoders[first][1] } } else { if !design.IsGoaEncoder(p) { factory = "DecoderFactory" } else { factory = design.KnownEncoders[first][2] } } d := &EncoderTemplateData{ PackagePath: p, PackageName: pkgName, Factory: factory, MIMETypes: mimeTypes, Default: isDefault, } data[p] = d } return data, nil }