Example #1
0
// generateContexts iterates through the version resources and actions and generates the action
// contexts.
func (g *Generator) generateContexts(verdir string, api *design.APIDefinition, version *design.APIVersionDefinition) error {
	ctxFile := filepath.Join(verdir, "contexts.go")
	ctxWr, err := NewContextsWriter(ctxFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Contexts", version.Context())
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("strconv"),
		codegen.SimpleImport("github.com/raphael/goa"),
	}
	if !version.IsDefault() {
		appPkg, err := AppPackagePath()
		if err != nil {
			return err
		}
		imports = append(imports, codegen.SimpleImport(filepath.ToSlash(appPkg)))
	}
	ctxWr.WriteHeader(title, packageName(version), imports)
	var appPackage string
	if !version.IsDefault() {
		appPackage = TargetPackage
	}
	err = version.IterateResources(func(r *design.ResourceDefinition) error {
		if !r.SupportsVersion(version.Version) {
			return nil
		}
		return r.IterateActions(func(a *design.ActionDefinition) error {
			ctxName := codegen.Goify(a.Name, true) + codegen.Goify(a.Parent.Name, true) + "Context"
			ctxData := ContextTemplateData{
				Name:         ctxName,
				ResourceName: r.Name,
				ActionName:   a.Name,
				Payload:      a.Payload,
				Params:       a.AllParams(),
				Headers:      r.Headers.Merge(a.Headers),
				Routes:       a.Routes,
				Responses:    MergeResponses(r.Responses, a.Responses),
				API:          api,
				Version:      version,
				AppPackage:   appPackage,
			}
			return ctxWr.Execute(&ctxData)
		})
	})
	g.genfiles = append(g.genfiles, ctxFile)
	if err != nil {
		return err
	}
	return ctxWr.FormatCode()
}
Example #2
0
// 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("github.com/julienschmidt/httprouter"),
		codegen.SimpleImport("github.com/raphael/goa"),
	}
	if !version.IsDefault() {
		appPkg, err := AppPackagePath()
		if err != nil {
			return err
		}
		imports = append(imports, codegen.SimpleImport(appPkg))
	}
	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{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))
			action := map[string]interface{}{
				"Name":    codegen.Goify(a.Name, true),
				"Routes":  a.Routes,
				"Context": context,
			}
			data.Actions = append(data.Actions, action)
			return nil
		})
		if err != nil {
			return err
		}
		if len(data.Actions) > 0 {
			data.Version = 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()
}
Example #3
0
// Generated package name for resources supporting the given version.
func packageName(version *design.APIVersionDefinition) (pack string) {
	pack = TargetPackage
	if version.Version != "" {
		pack = codegen.Goify(codegen.VersionPackage(version.Version), false)
	}
	return
}
Example #4
0
// newCoerceData is a helper function that creates a map that can be given to the "Coerce" template.
func newCoerceData(name string, att *design.AttributeDefinition, pkg string, depth int) map[string]interface{} {
	return map[string]interface{}{
		"Name":      name,
		"VarName":   codegen.Goify(name, false),
		"Attribute": att,
		"Pkg":       pkg,
		"Depth":     depth,
	}
}
Example #5
0
// joinNames is a code generation helper function that generates a string built from concatenating
// the keys of the given attribute type (assuming it's an object).
func joinNames(att *design.AttributeDefinition) string {
	if att == nil {
		return ""
	}
	obj := att.Type.ToObject()
	names := make([]string, len(obj))
	i := 0
	for n := range obj {
		names[i] = fmt.Sprintf("cmd.%s", codegen.Goify(n, true))
		i++
	}
	sort.Strings(names)
	return strings.Join(names, ", ")
}
Example #6
0
// generateHrefs iterates through the version resources and generates the href factory methods.
func (g *Generator) generateHrefs(verdir string, version *design.APIVersionDefinition) error {
	hrefFile := filepath.Join(verdir, "hrefs.go")
	resWr, err := NewResourcesWriter(hrefFile)
	if err != nil {
		panic(err) // bug
	}
	title := fmt.Sprintf("%s: Application Resource Href Factories", version.Context())
	resWr.WriteHeader(title, packageName(version), nil)
	err = version.IterateResources(func(r *design.ResourceDefinition) error {
		if !r.SupportsVersion(version.Version) {
			return nil
		}
		m := design.Design.MediaTypeWithIdentifier(r.MediaType)
		var identifier string
		if m != nil {
			identifier = m.Identifier
		} else {
			identifier = "plain/text"
		}
		canoTemplate := r.URITemplate(version)
		canoTemplate = design.WildcardRegex.ReplaceAllLiteralString(canoTemplate, "/%v")
		var canoParams []string
		if ca := r.CanonicalAction(); ca != nil {
			if len(ca.Routes) > 0 {
				canoParams = ca.Routes[0].Params(version)
			}
		}

		data := ResourceData{
			Name:              codegen.Goify(r.Name, true),
			Identifier:        identifier,
			Description:       r.Description,
			Type:              m,
			CanonicalTemplate: canoTemplate,
			CanonicalParams:   canoParams,
		}
		return resWr.Execute(&data)
	})
	g.genfiles = append(g.genfiles, hrefFile)
	if err != nil {
		return err
	}
	return resWr.FormatCode()
}
Example #7
0
// 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,
		"goify":                codegen.Goify,
		"okResp":               okResp,
		"newControllerVersion": newControllerVersion,
		"versionPkg":           codegen.VersionPackage,
		"targetPkg":            func() string { return TargetPackage },
	}
	gopath := filepath.SplitList(os.Getenv("GOPATH"))[0]
	if err != nil {
		var tmpl *template.Template
		tmpl, err = template.New("main").Funcs(funcs).Parse(mainTmpl)
		if err != nil {
			panic(err.Error()) // bug
		}
		gg := codegen.NewGoGenerator(mainFile)
		var outPkg string
		outPkg, err = filepath.Rel(gopath, codegen.OutputDir)
		if err != nil {
			return
		}
		outPkg = strings.TrimPrefix(filepath.ToSlash(outPkg), "src/")
		appPkg := path.Join(outPkg, "app")
		swaggerPkg := path.Join(outPkg, "swagger")
		imports := []*codegen.ImportSpec{
			codegen.SimpleImport("github.com/raphael/goa"),
			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))
		}
		gg.WriteHeader("", "main", imports)
		data := map[string]interface{}{
			"Name": AppName,
			"API":  api,
		}
		if err = tmpl.Execute(gg, data); err != nil {
			return
		}
		if err = gg.FormatCode(); err != nil {
			return
		}
	}
	tmpl, err := template.New("ctrl").Funcs(funcs).Parse(ctrlTmpl)
	if err != nil {
		panic(err.Error()) // bug
	}
	imp, err := filepath.Rel(filepath.Join(gopath, "src"), 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.Goify(v.Version, false)))
		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 {
			resGen := codegen.NewGoGenerator(filename)
			resGen.WriteHeader("", "main", imports)
			err := tmpl.Execute(resGen, r)
			if err != nil {
				return err
			}
			if err := resGen.FormatCode(); err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return
	}

	return g.genfiles, nil
}
Example #8
0
// Generate the application code, implement codegen.Generator.
func (g *Generator) Generate(api *design.APIDefinition) ([]string, error) {
	if api == nil {
		return nil, fmt.Errorf("missing API definition, make sure design.Design is properly initialized")
	}
	title := fmt.Sprintf("%s: Application Contexts", api.Name)
	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/raphael/goa"),
		codegen.SimpleImport("strconv"),
	}
	g.ContextsWriter.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"
			ctxData := ContextTemplateData{
				Name:         ctxName,
				ResourceName: r.Name,
				ActionName:   a.Name,
				Payload:      a.Payload,
				Params:       a.AllParams(),
				Headers:      r.Headers.Merge(a.Headers),
				Routes:       a.Routes,
				Responses:    MergeResponses(r.Responses, a.Responses),
				MediaTypes:   api.MediaTypes,
				Types:        api.Types,
			}
			return g.ContextsWriter.Execute(&ctxData)
		})
	})
	g.genfiles = append(g.genfiles, g.contextsFilename)
	if err != nil {
		g.Cleanup()
		return nil, err
	}
	if err := g.ContextsWriter.FormatCode(); err != nil {
		g.Cleanup()
		return nil, err
	}

	title = fmt.Sprintf("%s: Application Controllers", api.Name)
	imports = []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/julienschmidt/httprouter"),
		codegen.SimpleImport("github.com/raphael/goa"),
	}
	g.ControllersWriter.WriteHeader(title, TargetPackage, imports)
	var controllersData []*ControllerTemplateData
	api.IterateResources(func(r *design.ResourceDefinition) error {
		data := &ControllerTemplateData{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))
			action := map[string]interface{}{
				"Name":    codegen.Goify(a.Name, true),
				"Routes":  a.Routes,
				"Context": context,
			}
			data.Actions = append(data.Actions, action)
			return nil
		})
		if err != nil {
			return err
		}
		if len(data.Actions) > 0 {
			controllersData = append(controllersData, data)
		}
		return nil
	})
	g.genfiles = append(g.genfiles, g.controllersFilename)
	if err := g.ControllersWriter.Execute(controllersData); err != nil {
		g.Cleanup()
		return nil, err
	}
	if err := g.ControllersWriter.FormatCode(); err != nil {
		g.Cleanup()
		return nil, err
	}

	title = fmt.Sprintf("%s: Application Resource Href Factories", api.Name)
	g.ResourcesWriter.WriteHeader(title, TargetPackage, nil)
	err = api.IterateResources(func(r *design.ResourceDefinition) error {
		m, ok := api.MediaTypes[r.MediaType]
		var identifier string
		if ok {
			identifier = m.Identifier
		} else {
			identifier = "application/text"
		}
		canoTemplate := r.URITemplate()
		canoTemplate = design.WildcardRegex.ReplaceAllLiteralString(canoTemplate, "/%v")
		var canoParams []string
		if ca := r.CanonicalAction(); ca != nil {
			if len(ca.Routes) > 0 {
				canoParams = ca.Routes[0].Params()
			}
		}

		data := ResourceData{
			Name:              codegen.Goify(r.Name, true),
			Identifier:        identifier,
			Description:       r.Description,
			Type:              m,
			CanonicalTemplate: canoTemplate,
			CanonicalParams:   canoParams,
		}
		return g.ResourcesWriter.Execute(&data)
	})
	g.genfiles = append(g.genfiles, g.resourcesFilename)
	if err != nil {
		g.Cleanup()
		return nil, err
	}
	if err := g.ResourcesWriter.FormatCode(); err != nil {
		g.Cleanup()
		return nil, err
	}

	title = fmt.Sprintf("%s: Application Media Types", api.Name)
	imports = []*codegen.ImportSpec{
		codegen.SimpleImport("github.com/raphael/goa"),
		codegen.SimpleImport("fmt"),
	}
	g.MediaTypesWriter.WriteHeader(title, TargetPackage, imports)
	err = api.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error {
		if mt.Type.IsObject() || mt.Type.IsArray() {
			return g.MediaTypesWriter.Execute(mt)
		}
		return nil
	})
	g.genfiles = append(g.genfiles, g.mediaTypesFilename)
	if err != nil {
		g.Cleanup()
		return nil, err
	}
	if err := g.MediaTypesWriter.FormatCode(); err != nil {
		g.Cleanup()
		return nil, err
	}

	title = fmt.Sprintf("%s: Application User Types", api.Name)
	g.UserTypesWriter.WriteHeader(title, TargetPackage, nil)
	err = api.IterateUserTypes(func(t *design.UserTypeDefinition) error {
		return g.UserTypesWriter.Execute(t)
	})
	g.genfiles = append(g.genfiles, g.userTypesFilename)
	if err != nil {
		g.Cleanup()
		return nil, err
	}
	if err := g.UserTypesWriter.FormatCode(); err != nil {
		g.Cleanup()
		return nil, err
	}

	return g.genfiles, nil
}