// BelongsTo signifies a relationship between this model and a // Parent. The Parent has the child, and the Child belongs // to the Parent. // Usage: BelongsTo("User") func BelongsTo(parent string) { if r, ok := relationalModelDefinition(false); ok { idfield := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(parent), true) + "ID", Description: "Belongs To " + codegen.Goify(inflect.Singularize(parent), true), Parent: r, Datatype: gorma.BelongsTo, DatabaseFieldName: SanitizeDBFieldName(codegen.Goify(inflect.Singularize(parent), true) + "ID"), } r.RelationalFields[idfield.Name] = idfield bt, ok := r.Parent.RelationalModels[codegen.Goify(inflect.Singularize(parent), true)] if ok { r.BelongsTo[bt.Name] = bt } else { models := &gorma.RelationalModelDefinition{ Name: codegen.Goify(inflect.Singularize(parent), true), Parent: r.Parent, RelationalFields: make(map[string]*gorma.RelationalFieldDefinition), BelongsTo: make(map[string]*gorma.RelationalModelDefinition), HasMany: make(map[string]*gorma.RelationalModelDefinition), HasOne: make(map[string]*gorma.RelationalModelDefinition), ManyToMany: make(map[string]*gorma.ManyToManyDefinition), } r.BelongsTo[models.Name] = models } } }
// 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("golang.org/x/net/context"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("github.com/goadesign/goa"), } if !version.IsDefault() { appPkg, err := AppPackagePath() if err != nil { return err } imports = append(imports, codegen.SimpleImport(appPkg)) } ctxWr.WriteHeader(title, packageName(version), imports) 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" 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: MergeResponses(r.Responses, a.Responses), API: api, Version: version, DefaultPkg: TargetPackage, } return ctxWr.Execute(&ctxData) }) }) g.genfiles = append(g.genfiles, ctxFile) if err != nil { return err } return ctxWr.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() }
// Execute writes the code for the context types to the writer. func (w *ContextsWriter) Execute(data *ContextTemplateData) error { fn := template.FuncMap{ "hasAPIVersion": hasAPIVersion, } if err := w.ExecuteTemplate("context", ctxT, fn, data); err != nil { return err } fn = template.FuncMap{ "newCoerceData": newCoerceData, "arrayAttribute": arrayAttribute, } if err := w.ExecuteTemplate("new", ctxNewT, fn, data); err != nil { return err } if data.Payload != nil { if err := w.ExecuteTemplate("payload", payloadT, nil, data); err != nil { return err } } fn = template.FuncMap{ "project": func(mt *design.MediaTypeDefinition, v string) *design.MediaTypeDefinition { p, _, _ := mt.Project(v) return p }, } data.IterateResponses(func(resp *design.ResponseDefinition) error { respData := map[string]interface{}{ "Context": data, "Response": resp, } if resp.Type != nil { respData["Type"] = resp.Type if err := w.ExecuteTemplate("response", ctxTRespT, fn, respData); err != nil { return err } } else if mt := design.Design.MediaTypeWithIdentifier(resp.MediaType); mt != nil { respData["MediaType"] = mt fn["respName"] = func(resp *design.ResponseDefinition, view string) string { if view == "default" { return codegen.Goify(resp.Name, true) } base := fmt.Sprintf("%s%s", resp.Name, strings.Title(view)) return codegen.Goify(base, true) } if err := w.ExecuteTemplate("response", ctxMTRespT, fn, respData); err != nil { return err } } else { if err := w.ExecuteTemplate("response", ctxNoMTRespT, fn, respData); err != nil { return err } } return nil }) return nil }
// 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() }
// HasMany signifies a relationship between this model and a // set of Children. The Parent has the children, and the Children belong // to the Parent. The first parameter becomes the name of the // field in the model struct, the second parameter is the name // of the child model. The Child model will have a ParentID field // appended to the field list. The Parent model definition will use // the first parameter as the field name in the struct definition. // Usage: HasMany("Orders", "Order") // Struct field definition: Children []Child func HasMany(name, child string) { if r, ok := relationalModelDefinition(false); ok { field := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(name, true), HasMany: child, Description: "has many " + inflect.Pluralize(child), Datatype: gorma.HasMany, Parent: r, } r.RelationalFields[field.Name] = field var model *gorma.RelationalModelDefinition model, ok := r.Parent.RelationalModels[child] if ok { r.HasMany[child] = model // create the fk field f := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(r.Name), true) + "ID", HasMany: child, Description: "has many " + child, Datatype: gorma.HasManyKey, Parent: model, DatabaseFieldName: SanitizeDBFieldName(codegen.Goify(inflect.Singularize(r.Name), true) + "ID"), } model.RelationalFields[f.Name] = f } else { model = &gorma.RelationalModelDefinition{ Name: child, Parent: r.Parent, RelationalFields: make(map[string]*gorma.RelationalFieldDefinition), BelongsTo: make(map[string]*gorma.RelationalModelDefinition), HasMany: make(map[string]*gorma.RelationalModelDefinition), HasOne: make(map[string]*gorma.RelationalModelDefinition), ManyToMany: make(map[string]*gorma.ManyToManyDefinition), } r.HasMany[child] = model // create the fk field f := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(r.Name), true) + "ID", HasMany: child, Description: "has many " + child, Datatype: gorma.HasManyKey, Parent: model, DatabaseFieldName: SanitizeDBFieldName(codegen.Goify(inflect.Singularize(r.Name), true) + "ID"), } model.RelationalFields[f.Name] = f } } }
// Generate is the generator entry point called by the meta generator. func Generate() (files []string, err error) { var ( outDir, target, ver string notest bool ) set := flag.NewFlagSet("app", flag.PanicOnError) set.String("design", "", "") set.StringVar(&outDir, "out", "", "") set.StringVar(&target, "pkg", "app", "") set.StringVar(&ver, "version", "", "") set.BoolVar(¬est, "notest", false, "") set.Bool("force", false, "") set.Parse(os.Args[1:]) outDir = filepath.Join(outDir, target) if err := codegen.CheckVersion(ver); err != nil { return nil, err } target = codegen.Goify(target, false) g := &Generator{OutDir: outDir, Target: target, NoTest: notest, API: design.Design, validator: codegen.NewValidator()} return g.Generate() }
// return a ',' joined list of Params as a reference to cmd.XFieldName // ordered by the required first rules. func joinFieldhNames(atts ...*design.AttributeDefinition) string { var elems []string for _, att := range atts { if att == nil { continue } obj := att.Type.ToObject() var names, optNames []string keys := make([]string, len(obj)) i := 0 for n := range obj { keys[i] = n i++ } sort.Strings(keys) for _, n := range keys { field := fmt.Sprintf("cmd.%s", codegen.Goify(n, true)) if att.IsRequired(n) { names = append(names, field) } else { optNames = append(optNames, field) } } elems = append(elems, names...) elems = append(elems, optNames...) } return strings.Join(elems, ", ") }
// Generate is the generator entry point called by the meta generator. func Generate() (files []string, err error) { var ( outDir, target, toolDir, tool, ver string notool bool ) dtool := defaultToolName(design.Design) set := flag.NewFlagSet("client", flag.PanicOnError) set.String("design", "", "") set.StringVar(&outDir, "out", "", "") set.StringVar(&target, "pkg", "client", "") set.StringVar(&toolDir, "tooldir", "tool", "") set.StringVar(&tool, "tool", dtool, "") set.StringVar(&ver, "version", "", "") set.BoolVar(¬ool, "notool", false, "") set.Parse(os.Args[1:]) // First check compatibility if err := codegen.CheckVersion(ver); err != nil { return nil, err } // Now proceed target = codegen.Goify(target, false) g := &Generator{OutDir: outDir, Target: target, ToolDirName: toolDir, Tool: tool, NoTool: notool, API: design.Design} return g.Generate() }
// 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 }
// PopulateFromModeledType creates fields for the model // based on the goa UserTypeDefinition it models. // This happens before fields are processed, so it's // ok to just assign without testing. func (f *RelationalModelDefinition) PopulateFromModeledType() { if f.BuiltFrom == nil { return } for _, mt := range f.BuiltFrom { obj := mt.ToObject() obj.IterateAttributes(func(name string, att *design.AttributeDefinition) error { rf := &RelationalFieldDefinition{} rf.Parent = f rf.Name = codegen.Goify(name, true) if strings.HasSuffix(rf.Name, "Id") { rf.Name = strings.TrimSuffix(rf.Name, "Id") rf.Name = rf.Name + "ID" } switch att.Type.Kind() { case design.BooleanKind: rf.Datatype = Boolean case design.IntegerKind: rf.Datatype = Integer case design.NumberKind: rf.Datatype = Decimal case design.StringKind: rf.Datatype = String case design.DateTimeKind: rf.Datatype = Timestamp default: dslengine.ReportError("Unsupported type: %#v ", att.Type.Kind()) } f.RelationalFields[rf.Name] = rf return nil }) } return }
// generateHrefs iterates through the API resources and generates the href factory methods. func (g *Generator) generateHrefs(api *design.APIDefinition) error { hrefFile := filepath.Join(AppOutputDir(), "hrefs.go") resWr, err := NewResourcesWriter(hrefFile) if err != nil { panic(err) // bug } title := fmt.Sprintf("%s: Application Resource Href Factories", api.Context()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("fmt"), } resWr.WriteHeader(title, TargetPackage, imports) err = api.IterateResources(func(r *design.ResourceDefinition) error { m := api.MediaTypeWithIdentifier(r.MediaType) var identifier string if m != nil { identifier = m.Identifier } else { identifier = "text/plain" } data := ResourceData{ Name: codegen.Goify(r.Name, true), Identifier: identifier, Description: r.Description, Type: m, CanonicalTemplate: codegen.CanonicalTemplate(r), CanonicalParams: codegen.CanonicalParams(r), } return resWr.Execute(&data) }) g.genfiles = append(g.genfiles, hrefFile) if err != nil { return err } return resWr.FormatCode() }
// join is a code generation helper function that generates a function signature built from // concatenating the properties (name type) of the given attribute type (assuming it's an object). // join accepts an optional slice of strings which indicates the order in which the parameters // should appear in the signature. If pos is specified then it must list all the parameters. If // it's not specified then parameters are sorted alphabetically. func join(att *design.AttributeDefinition, pos ...[]string) string { if att == nil { return "" } obj := att.Type.ToObject() elems := make([]string, len(obj)) var keys []string if len(pos) > 0 { keys = pos[0] if len(keys) != len(obj) { panic("invalid position slice, lenght does not match attribute field count") // bug } } else { keys = make([]string, len(obj)) i := 0 for n := range obj { keys[i] = n i++ } sort.Strings(keys) } for i, n := range keys { a := obj[n] elems[i] = fmt.Sprintf("%s %s", codegen.Goify(n, false), cmdFieldType(a.Type)) } return strings.Join(elems, ", ") }
// PKAttributes constructs a pair of field + definition strings // useful for method parameters. func (f *RelationalModelDefinition) PKAttributes() string { var attr []string for _, pk := range f.PrimaryKeys { attr = append(attr, fmt.Sprintf("%s %s", codegen.Goify(pk.DatabaseFieldName, false), goDatatype(pk, true))) } return strings.Join(attr, ",") }
// HasOne signifies a relationship between this model and another model. // If this model HasOne(OtherModel), then OtherModel is expected // to have a ThisModelID field as a Foreign Key to this model's // Primary Key. ThisModel will have a field named OtherModel of type // OtherModel. // Usage: HasOne("Proposal") func HasOne(child string) { if r, ok := relationalModelDefinition(false); ok { field := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(child), true), HasOne: child, Description: "has one " + child, Datatype: gorma.HasOne, Parent: r, } r.RelationalFields[field.Name] = field bt, ok := r.Parent.RelationalModels[child] if ok { r.HasOne[child] = bt // create the fk field f := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(r.Name), true) + "ID", HasOne: child, Description: "has one " + child, Datatype: gorma.HasOneKey, Parent: bt, DatabaseFieldName: SanitizeDBFieldName(codegen.Goify(inflect.Singularize(r.Name), true) + "ID"), } bt.RelationalFields[f.Name] = f } else { models := &gorma.RelationalModelDefinition{ Name: child, Parent: r.Parent, RelationalFields: make(map[string]*gorma.RelationalFieldDefinition), BelongsTo: make(map[string]*gorma.RelationalModelDefinition), HasMany: make(map[string]*gorma.RelationalModelDefinition), HasOne: make(map[string]*gorma.RelationalModelDefinition), ManyToMany: make(map[string]*gorma.ManyToManyDefinition), } r.HasOne[child] = models // create the fk field f := &gorma.RelationalFieldDefinition{ Name: codegen.Goify(inflect.Singularize(r.Name), true) + "ID", HasOne: child, Description: "has one " + child, Datatype: gorma.HasOneKey, Parent: bt, DatabaseFieldName: SanitizeDBFieldName(codegen.Goify(inflect.Singularize(r.Name), true) + "ID"), } models.RelationalFields[f.Name] = f } } }
// pathParamNames return the names of the parameters of the path factory function for the given route. func pathParamNames(r *design.RouteDefinition) string { params := r.Params() goified := make([]string, len(params)) for i, p := range params { goified[i] = codegen.Goify(p, false) } return strings.Join(goified, ", ") }
// PKWhereFields returns the fields for a where clause for the primary // keys of a model. func (f *RelationalModelDefinition) PKWhereFields() string { var pkwhere []string for _, pk := range f.PrimaryKeys { def := fmt.Sprintf("%s", codegen.Goify(pk.DatabaseFieldName, false)) pkwhere = append(pkwhere, def) } return strings.Join(pkwhere, ",") }
func viewFieldNames(ut *RelationalModelDefinition, v *design.ViewDefinition) []string { obj := v.Type.(design.Object) var fields []string for name := range obj { if obj[name].Type.IsPrimitive() { if strings.TrimSpace(name) != "" && name != "links" { bf, ok := ut.RelationalFields[codegen.Goify(name, true)] if ok { fields = append(fields, "&"+codegen.Goify(bf.FieldName, false)) } } } } sort.Strings(fields) return fields }
// SanitizeFieldName is exported for testing purposes func SanitizeFieldName(name string) string { name = codegen.Goify(name, true) if strings.HasSuffix(name, "Id") { name = strings.TrimSuffix(name, "Id") name = name + "ID" } return name }
// 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 }
// newCoerceData is a helper function that creates a map that can be given to the "Coerce" template. func newCoerceData(name string, att *design.AttributeDefinition, pointer bool, pkg string, depth int) map[string]interface{} { return map[string]interface{}{ "Name": name, "VarName": codegen.Goify(name, false), "Pointer": pointer, "Attribute": att, "Pkg": pkg, "Depth": depth, } }
func attToObject(name string, parent, att *design.AttributeDefinition) *ObjectType { obj := &ObjectType{} obj.Label = name obj.Name = codegen.Goify(name, false) obj.Type = codegen.GoTypeRef(att.Type, nil, 0, false) if att.Type.IsPrimitive() && parent.IsPrimitivePointer(name) { obj.Pointer = "*" } return obj }
// MediaTypeRef produces the JSON reference to the media type definition with the given view. func MediaTypeRef(api *design.APIDefinition, mt *design.MediaTypeDefinition, view string) string { if _, ok := Definitions[mt.TypeName]; !ok { GenerateMediaTypeDefinition(api, mt, view) } ref := fmt.Sprintf("#/definitions/%s", mt.TypeName) if view != "default" { ref += codegen.Goify(view, true) } return ref }
// PKUpdateFields returns something? This function doesn't look useful in // current form. Perhaps it isn't. func (f *RelationalModelDefinition) PKUpdateFields(modelname string) string { var pkwhere []string for _, pk := range f.PrimaryKeys { def := fmt.Sprintf("%s.%s", modelname, codegen.Goify(pk.FieldName, true)) pkwhere = append(pkwhere, def) } pkw := strings.Join(pkwhere, ",") return pkw }
// fileServerMethod returns the name of the client method for downloading assets served by the given // file server. // Note: the implementation opts for generating good names rather than names that are guaranteed to // be unique. This means that the generated code could be potentially incorrect in the rare cases // where it produces the same names for two different file servers. This should be addressed later // (when it comes up?) using metadata to let users override the default. func (g *Generator) fileServerMethod(fs *design.FileServerDefinition) string { var ( suffix string wcs = design.ExtractWildcards(fs.RequestPath) reqElems = strings.Split(fs.RequestPath, "/") ) if len(wcs) == 0 { suffix = path.Base(fs.RequestPath) ext := filepath.Ext(suffix) suffix = strings.TrimSuffix(suffix, ext) suffix += codegen.Goify(ext, true) } else { if len(reqElems) == 1 { suffix = filepath.Base(fs.RequestPath) suffix = suffix[1:] // remove "*" prefix } else { suffix = reqElems[len(reqElems)-2] // should work most of the time } } return "Download" + codegen.Goify(suffix, true) }
// 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, ", ") }
func (g *Generator) okResp(a *design.ActionDefinition) map[string]interface{} { var ok *design.ResponseDefinition for _, resp := range a.Responses { if resp.Status == 200 { ok = resp break } } if ok == nil { return nil } var mt *design.MediaTypeDefinition var ok2 bool if mt, ok2 = design.Design.MediaTypes[design.CanonicalIdentifier(ok.MediaType)]; !ok2 { return nil } view := ok.ViewName if view == "" { view = design.DefaultView } pmt, _, err := mt.Project(view) if err != nil { return nil } var typeref string if pmt.IsError() { typeref = `goa.ErrInternal("not implemented")` } else { name := codegen.GoTypeRef(pmt, pmt.AllRequired(), 1, false) var pointer string if strings.HasPrefix(name, "*") { name = name[1:] pointer = "*" } typeref = fmt.Sprintf("%s%s.%s", pointer, g.Target, name) if strings.HasPrefix(typeref, "*") { typeref = "&" + typeref[1:] } typeref += "{}" } var nameSuffix string if view != "default" { nameSuffix = codegen.Goify(view, true) } return map[string]interface{}{ "Name": ok.Name + nameSuffix, "GoType": codegen.GoNativeType(pmt), "TypeRef": typeref, } }
// hasAPIVersion returns true if the given attribute has a child attribute whose goified name is // "APIVersion". This is used to not generate the built in APIVersion when such a field exists. func hasAPIVersion(params *design.AttributeDefinition) bool { if params == nil { return false } o := params.Type.ToObject() if o == nil { return false } for n := range o { if codegen.Goify(n, true) == "APIVersion" { return true } } return false }
func viewSelect(ut *RelationalModelDefinition, v *design.ViewDefinition) string { obj := v.Type.(design.Object) var fields []string for name := range obj { if obj[name].Type.IsPrimitive() { if strings.TrimSpace(name) != "" && name != "links" { bf, ok := ut.RelationalFields[codegen.Goify(name, true)] if ok { fields = append(fields, bf.DatabaseFieldName) } } } } sort.Strings(fields) return strings.Join(fields, ",") }
// 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()) imports := []*codegen.ImportSpec{ codegen.SimpleImport("fmt"), } resWr.WriteHeader(title, packageName(version), imports) 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() }