// Analyze an attribute, create corresponding ActionParam func (a *ApiAnalyzer) AnalyzeAttribute(name, query string, attr map[string]interface{}) (*gen.ActionParam, error) { param := gen.ActionParam{Name: name, QueryName: query, VarName: toVarName(name)} if d, ok := attr["description"]; ok { param.Description = removeBlankLines(d.(string)) } if r, ok := attr["required"]; ok { if r.(bool) { param.Mandatory = true } } if options, ok := attr["options"]; ok { opts, ok := options.(map[string]interface{}) if ok { for n, o := range opts { switch n { case "max": param.Max = int(o.(float64)) case "min": param.Min = int(o.(float64)) case "regexp": param.Regexp = o.(string) } } } } if values, ok := attr["values"]; ok { param.ValidValues = values.([]interface{}) } t := attr["type"].(map[string]interface{}) dataType, err := a.AnalyzeType(t, query) if err != nil { return nil, err } param.Type = dataType switch dataType.(type) { case *gen.ArrayDataType: param.QueryName += "[]" } return ¶m, nil }
// 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), } }