// 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 } } }
func pluralize(c int, s string, format bool) string { if c == 1 { if format { return fmt.Sprintf("1 %s", inflect.Singularize(s)) } return fmt.Sprintf("%s", inflect.Singularize(s)) } if format { return fmt.Sprintf("%d %s", c, inflect.Pluralize(s)) } return fmt.Sprintf("%s", inflect.Pluralize(s)) }
// 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 } } }
func createDescriptor(iface interface{}, router *ResourceRouter) descriptor { pv := reflect.ValueOf(iface) v := reflect.Indirect(pv) t := v.Type() var varName string if nameGetter, hasVarName := iface.(interface { VarName() string }); hasVarName { varName = nameGetter.VarName() } else { varName = inflect.Singularize(t.Name()) } var routeName string if nameGetter, hasRouteName := iface.(interface { RouteName() string }); hasRouteName { routeName = nameGetter.RouteName() } else { routeName = strings.ToLower(t.Name()) } return descriptor{ obj: iface, router: router, t: t, varName: varName, routeName: routeName, } }
// singularize returns the singular form of a single word. func singularize(in interface{}) (string, error) { word, err := cast.ToStringE(in) if err != nil { return "", err } return inflect.Singularize(word), nil }
func parseReturn(kind, resName, contentType string) string { switch kind { case "show": return refType(resName) case "index": return fmt.Sprintf("[]%s", refType(resName)) case "create": if _, ok := noMediaTypeResources[resName]; ok { return "map[string]interface{}" } return "*" + inflect.Singularize(resName) + "Locator" case "update", "destroy": return "" case "current_instances": return "[]*Instance" default: switch { case len(contentType) == 0: return "" case strings.Index(contentType, "application/vnd.rightscale.") == 0: if contentType == "application/vnd.rightscale.text" { return "string" } elems := strings.SplitN(contentType[27:], ";", 2) name := refType(inflect.Camelize(elems[0])) if len(elems) > 1 && elems[1] == "type=collection" { name = "[]" + refType(inflect.Camelize(elems[0])) } return name default: // Shouldn't be here panic("api15gen: Unknown content type " + contentType) } } }
// 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 } } }
// Name of go type for resource with given name // It should always be the same (camelized) but there are some resources that don't have a media // type so for these we use a map. func resourceType(resName string) string { if resName == "ChildAccounts" { return "Account" } if _, ok := noMediaTypeResources[resName]; ok { return "map[string]interface{}" } return inflect.Singularize(resName) }
func serviceResource(svc *Service, resource string, w http.ResponseWriter) (*Resource, error) { res, ok := svc.Resources[inflect.Singularize(resource)] if !ok { return nil, httpError(w, 404, "Sorry, %s does not have resource %s, available resources: %+v", svc.Name, resource, svc.ResourceNames()) } return res, nil }
func serviceResource(svc *Service, resource string, w http.ResponseWriter) (*Resource, error) { res, ok := svc.Resources[inflect.Singularize(resource)] if !ok { w.WriteHeader(404) fmt.Fprintf(w, "Sorry, %s does not have resource %s, available resources: %+v", svc.Name, resource, svc.ResourceNames()) return nil, fmt.Errorf("%s does not have resource %s", svc.Name, resource) } return res, nil }
// build the path that precedes the controller name func buildParentPath(parentControllers []string) (string, error) { path := "/" for i := len(parentControllers) - 1; i >= 0; i-- { c := parentControllers[i] name, err := getResourceName(c) if err != nil { return "", err } path += strings.ToLower(name) + "/:" + inflect.Singularize(name) + "Id/" } return path, nil }
// Create API descriptor from raw resources and types func (a *ApiAnalyzer) AnalyzeResource(name string, res map[string]interface{}, desc *gen.ApiDescriptor) error { name = inflect.Singularize(name) resource := gen.Resource{Name: name, ClientName: a.ClientName} // Description if d, ok := res["description"]; ok { resource.Description = removeBlankLines(d.(string)) } // Attributes hasHref := false attributes := []*gen.Attribute{} m, ok := res["media_type"].(string) if ok { t, ok := a.RawTypes[m] if ok { attrs, ok := t["attributes"].(map[string]interface{}) if ok { attributes = make([]*gen.Attribute, len(attrs)) for idx, n := range sortedKeys(attrs) { if n == "href" { hasHref = true } param, err := a.AnalyzeAttribute(n, n, attrs[n].(map[string]interface{})) if err != nil { return err } attributes[idx] = &gen.Attribute{n, inflect.Camelize(n), param.Signature()} } } } } resource.Attributes = attributes if hasHref { resource.LocatorFunc = locatorFunc(name) } // Actions actions, err := a.AnalyzeActions(name, res) if err != nil { return err } resource.Actions = actions // Name and done resName := toGoTypeName(name, false) desc.Resources[resName] = &resource desc.ResourceNames = append(desc.ResourceNames, resName) return nil }
func init() { funcMap = template.FuncMap{ "urlize": helpers.URLize, "sanitizeURL": helpers.SanitizeURL, "sanitizeurl": helpers.SanitizeURL, "eq": Eq, "ne": Ne, "gt": Gt, "ge": Ge, "lt": Lt, "le": Le, "dict": Dictionary, "in": In, "slicestr": Slicestr, "substr": Substr, "split": Split, "intersect": Intersect, "isSet": IsSet, "isset": IsSet, "echoParam": ReturnWhenSet, "safeHTML": SafeHTML, "safeCSS": SafeCSS, "safeURL": SafeURL, "absURL": func(a string) template.HTML { return template.HTML(helpers.AbsURL(a)) }, "relURL": func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) }, "markdownify": Markdownify, "first": First, "last": Last, "after": After, "where": Where, "delimit": Delimit, "sort": Sort, "highlight": Highlight, "add": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '+') }, "sub": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '-') }, "div": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '/') }, "mod": Mod, "mul": func(a, b interface{}) (interface{}, error) { return doArithmetic(a, b, '*') }, "modBool": ModBool, "lower": func(a string) string { return strings.ToLower(a) }, "upper": func(a string) string { return strings.ToUpper(a) }, "title": func(a string) string { return strings.Title(a) }, "partial": Partial, "ref": Ref, "relref": RelRef, "apply": Apply, "chomp": Chomp, "replace": Replace, "trim": Trim, "dateFormat": DateFormat, "getJSON": GetJSON, "getCSV": GetCSV, "readDir": ReadDir, "seq": helpers.Seq, "getenv": func(varName string) string { return os.Getenv(varName) }, "base64Decode": Base64Decode, "base64Encode": Base64Encode, "pluralize": func(in interface{}) (string, error) { word, err := cast.ToStringE(in) if err != nil { return "", err } return inflect.Pluralize(word), nil }, "singularize": func(in interface{}) (string, error) { word, err := cast.ToStringE(in) if err != nil { return "", err } return inflect.Singularize(word), nil }, } }
// writeSchema writes SQL statements to CREATE, INSERT, // UPDATE and DELETE values from Table t. func writeSchema(w io.Writer, d schema.Dialect, t *schema.Table) { writeConst(w, d.Table(t), "create", inflect.Singularize(t.Name), "stmt", ) writeConst(w, d.Insert(t), "insert", inflect.Singularize(t.Name), "stmt", ) writeConst(w, d.Select(t, nil), "select", inflect.Singularize(t.Name), "stmt", ) writeConst(w, d.SelectRange(t, nil), "select", inflect.Singularize(t.Name), "range", "stmt", ) writeConst(w, d.SelectCount(t, nil), "select", inflect.Singularize(t.Name), "count", "stmt", ) if len(t.Primary) != 0 { writeConst(w, d.Select(t, t.Primary), "select", inflect.Singularize(t.Name), "pkey", "stmt", ) writeConst(w, d.Update(t, t.Primary), "update", inflect.Singularize(t.Name), "pkey", "stmt", ) writeConst(w, d.Delete(t, t.Primary), "delete", inflect.Singularize(t.Name), "pkey", "stmt", ) } for _, ix := range t.Index { writeConst(w, d.Index(t, ix), "create", ix.Name, "stmt", ) writeConst(w, d.Select(t, ix.Fields), "select", ix.Name, "stmt", ) if !ix.Unique { writeConst(w, d.SelectRange(t, ix.Fields), "select", ix.Name, "range", "stmt", ) writeConst(w, d.SelectCount(t, ix.Fields), "select", ix.Name, "count", "stmt", ) } else { writeConst(w, d.Update(t, ix.Fields), "update", ix.Name, "stmt", ) writeConst(w, d.Delete(t, ix.Fields), "delete", ix.Name, "stmt", ) } } }
// singularize returns singular version of a word func singularize(s string) string { return inflect.Singularize(s) }
// AnalyzeResource analyzes the given resource and updates the Resources and ParamTypes analyzer // fields accordingly func (a *APIAnalyzer) AnalyzeResource(name string, resource interface{}, descriptor *gen.APIDescriptor) { var res = resource.(map[string]interface{}) // Compute description var description string if d, ok := res["description"].(string); ok { description = d } // Compute attributes var attributes []*gen.Attribute var atts map[string]interface{} if m, ok := res["media_type"].(map[string]interface{}); ok { atts = m["attributes"].(map[string]interface{}) attributes = make([]*gen.Attribute, len(atts)) for idx, n := range sortedKeys(atts) { at, ok := a.attributeTypes[n+"#"+name] if !ok { at = a.attributeTypes[n] } attributes[idx] = &gen.Attribute{n, inflect.Camelize(n), at} } } else { attributes = []*gen.Attribute{} } // Compute actions var methods = res["methods"].(map[string]interface{}) var actionNames = sortedKeys(methods) var actions = []*gen.Action{} for _, actionName := range actionNames { var m = methods[actionName] var meth = m.(map[string]interface{}) var params map[string]interface{} if p, ok := meth["parameters"]; ok { params = p.(map[string]interface{}) } var description = "No description provided for " + actionName + "." if d, _ := meth["description"]; d != nil { description = d.(string) } var pathPatterns = ParseRoute(fmt.Sprintf("%s#%s", name, actionName), meth["route"].(string)) if len(pathPatterns) == 0 { // Custom action continue } var allParamNames = make([]string, len(params)) var i = 0 for n := range params { allParamNames[i] = n i++ } sort.Strings(allParamNames) var contentType string if c, ok := meth["content_type"].(string); ok { contentType = c } var paramAnalyzer = NewAnalyzer(params) paramAnalyzer.Analyze() // Record new parameter types var paramTypeNames = make([]string, len(paramAnalyzer.ParamTypes)) var idx = 0 for n := range paramAnalyzer.ParamTypes { paramTypeNames[idx] = n idx++ } sort.Strings(paramTypeNames) for _, name := range paramTypeNames { var pType = paramAnalyzer.ParamTypes[name] if _, ok := a.rawTypes[name]; ok { a.rawTypes[name] = append(a.rawTypes[name], pType) } else { a.rawTypes[name] = []*gen.ObjectDataType{pType} } } // Update description with parameter descriptions var mandatory []string var optional []string for _, p := range paramAnalyzer.Params { if p.Mandatory { desc := p.Name if p.Description != "" { desc += ": " + strings.TrimSpace(p.Description) } mandatory = append(mandatory, desc) } else { desc := p.Name if p.Description != "" { desc += ": " + strings.TrimSpace(p.Description) } optional = append(optional, desc) } } if len(mandatory) > 0 { sort.Strings(mandatory) if !strings.HasSuffix(description, "\n") { description += "\n" } description += "Required parameters:\n\t" + strings.Join(mandatory, "\n\t") } if len(optional) > 0 { sort.Strings(optional) if !strings.HasSuffix(description, "\n") { description += "\n" } description += "Optional parameters:\n\t" + strings.Join(optional, "\n\t") } // Sort parameters by location actionParams := paramAnalyzer.Params leafParams := paramAnalyzer.LeafParams var pathParamNames []string var queryParamNames []string var payloadParamNames []string for _, p := range leafParams { n := p.Name if isQueryParam(n) { queryParamNames = append(queryParamNames, n) p.Location = gen.QueryParam } else if isPathParam(n, pathPatterns) { pathParamNames = append(pathParamNames, n) p.Location = gen.PathParam } else { payloadParamNames = append(payloadParamNames, n) p.Location = gen.PayloadParam } } for _, p := range actionParams { done := false for _, ap := range leafParams { if ap == p { done = true break } } if done { continue } n := p.Name if isQueryParam(n) { p.Location = gen.QueryParam } else if isPathParam(n, pathPatterns) { p.Location = gen.PathParam } else { p.Location = gen.PayloadParam } } // Mix in filters information if filters, ok := meth["filters"]; ok { var filterParam *gen.ActionParam for _, p := range actionParams { if p.Name == "filter" { filterParam = p break } } if filterParam != nil { values := sortedKeys(filters.(map[string]interface{})) ivalues := make([]interface{}, len(values)) for i, v := range values { ivalues[i] = v } filterParam.ValidValues = ivalues } } // Record action action := gen.Action{ Name: actionName, MethodName: inflect.Camelize(actionName), Description: removeBlankLines(description), ResourceName: inflect.Singularize(name), PathPatterns: pathPatterns, Params: actionParams, LeafParams: paramAnalyzer.LeafParams, Return: parseReturn(actionName, name, contentType), ReturnLocation: actionName == "create" && name != "Oauth2", PathParamNames: pathParamNames, QueryParamNames: queryParamNames, PayloadParamNames: payloadParamNames, } actions = append(actions, &action) } // We're done! name = inflect.Singularize(name) descriptor.Resources[name] = &gen.Resource{ Name: name, ClientName: "API", Description: removeBlankLines(description), Actions: actions, Attributes: attributes, LocatorFunc: LocatorFunc(attributes, name), } }
func fieldAssignmentModelToType(model *RelationalModelDefinition, ut *design.ViewDefinition, v, mtype, utype string) string { tmp := 1 var fieldAssignments []string if !strings.Contains(ut.Name, "link") { for ln, lnd := range ut.Parent.Links { ln = codegen.Goify(ln, true) s := inflect.Singularize(ln) var ifb string if lnd.MediaType().IsArray() { mt := codegen.Goify(lnd.MediaType().ToArray().ElemType.Type.(*design.MediaTypeDefinition).TypeName, true) + "LinkCollection" fa := make([]string, 4) fa[0] = fmt.Sprintf("tmp%d := make(app.%s, len(%s.%s))", tmp, mt, v, ln) fa[1] = fmt.Sprintf("for i, elem := range %s.%s {", v, ln) fa[2] = fmt.Sprintf(" tmp%d[i] = elem.%sTo%sLink()", tmp, s, s) fa[3] = fmt.Sprintf("}") ifb = strings.Join(fa, "\n") } else { ifb = fmt.Sprintf("tmp%d := %s.%s.%sTo%sLink()", tmp, v, ln, s, s) } fieldAssignments = append(fieldAssignments, ifb) ifd := fmt.Sprintf("%s.Links = &app.%sLinks{%s: tmp%d}", utype, codegen.Goify(utype, true), codegen.Goify(ln, true), tmp) fieldAssignments = append(fieldAssignments, ifd) tmp++ } } // Get a sortable slice of field names var keys []string for k := range model.RelationalFields { keys = append(keys, k) } sort.Strings(keys) for _, fname := range keys { field := model.RelationalFields[fname] var mpointer, upointer bool mpointer = field.Nullable obj := ut.Type.ToObject() definition := ut.Parent.Definition() if field.Datatype == "" { continue } for key := range obj { gfield := obj[key] if field.Underscore() == key || field.DatabaseFieldName == key { // this is our field if gfield.Type.IsObject() || definition.IsPrimitivePointer(key) { upointer = true } else { // set it explicitly because we're reusing the same bool upointer = false } if field.Datatype == HasOne { fa := fmt.Sprintf("%s.%s = %s.%s.%sTo%s()", utype, codegen.Goify(field.FieldName, true), v, codegen.Goify(field.FieldName, true), codegen.Goify(field.FieldName, true), codegen.Goify(field.FieldName, true)) fieldAssignments = append(fieldAssignments, fa) continue } prefix := "" if upointer && !mpointer { // ufield = &mfield prefix = "&" } else if mpointer && !upointer { // ufield = *mfield (rare if never?) prefix = "*" } else if !upointer && !mpointer { prefix = "" } /// test to see if it's a go object here and add the appending stuff if gfield.Type.IsObject() || gfield.Type.IsArray() { tmp++ ifa := fmt.Sprintf("for i%d := range %s.%s {", tmp, v, codegen.Goify(fname, true)) fieldAssignments = append(fieldAssignments, ifa) ifd := fmt.Sprintf("tmp%d := &%s.%s[i%d]", tmp, v, codegen.Goify(fname, true), tmp) fieldAssignments = append(fieldAssignments, ifd) ifb := fmt.Sprintf("%s.%s = append(%s.%s, tmp%d.%sTo%s())", utype, codegen.Goify(key, true), utype, codegen.Goify(key, true), tmp, inflect.Singularize(codegen.Goify(key, true)), inflect.Singularize(codegen.Goify(key, true))) fieldAssignments = append(fieldAssignments, ifb) ifc := fmt.Sprintf("}") fieldAssignments = append(fieldAssignments, ifc) } else { fa := fmt.Sprintf("\t%s.%s = %s%s.%s", utype, codegen.Goify(key, true), prefix, v, codegen.Goify(fname, true)) fieldAssignments = append(fieldAssignments, fa) } } else { fn := codegen.Goify(strings.Replace(field.FieldName, "ID", "", -1), false) if fn == key { gfield, ok := obj[fn] if ok { fa := fmt.Sprintf("tmp%d := &%s.%s", tmp, mtype, codegen.Goify(fn, true)) fieldAssignments = append(fieldAssignments, fa) var view string if gfield.View != "" { view = gfield.View } fa = fmt.Sprintf("%s.%s = tmp%d.%sTo%s%s() // %s", utype, codegen.Goify(fn, true), tmp, codegen.Goify(fn, true), codegen.Goify(fn, true), codegen.Goify(view, true)) fieldAssignments = append(fieldAssignments, fa) tmp++ } } } } } return strings.Join(fieldAssignments, "\n") }