// Parse data type in context func (p *ParamAnalyzer) parseDataType(path string, child *gen.ActionParam) gen.DataType { param := p.rawParams[path].(map[string]interface{}) class := "String" if c, ok := param["class"].(string); ok { class = c } var res gen.DataType switch class { case "Integer": i := gen.BasicDataType("int") res = &i case "String": s := gen.BasicDataType("string") res = &s case "Array": if child != nil { res = &gen.ArrayDataType{child} } else { s := gen.BasicDataType("string") p := p.newParam(fmt.Sprintf("%s[item]", path), map[string]interface{}{}, &s) res = &gen.ArrayDataType{p} } case "Enumerable": res = new(gen.EnumerableDataType) case "Hash": if current, ok := p.parsed[path]; ok { res = current.Type o := res.(*gen.ObjectDataType) o.Fields = appendSorted(o.Fields, child) } else { oname := p.typeName(path) res = &gen.ObjectDataType{oname, []*gen.ActionParam{child}} } } return res }
// Analyze type given its json definition func (a *ApiAnalyzer) AnalyzeType(typeDef map[string]interface{}, query string) (gen.DataType, error) { n, ok := typeDef["name"].(string) if !ok { n = "Struct" // Assume inline struct (e.g. payload types) } if strings.HasSuffix(n, "FileUpload") { // A little bit hacky but this is to make upload work with resticle // The idea is that a type named "FileUpload" is assumed to define a multipart // request with a file part. // The type must define a "name" and "filename" string fields. t, ok := a.RawTypes[n] if !ok { return nil, fmt.Errorf("Unknown type %s for %s", n, prettify(typeDef)) } attrs, ok := t["attributes"] if !ok { return nil, fmt.Errorf("Invalid file upload type %s for %s: no attributes", n, prettify(typeDef)) } mattrs, ok := attrs.(map[string]interface{}) if !ok { return nil, fmt.Errorf("Invalid file upload type %s for %s: basic type", n, prettify(typeDef)) } _, ok = mattrs["name"] if !ok { return nil, fmt.Errorf("Invalid file upload type %s for %s: no name", n, prettify(typeDef)) } _, ok = mattrs["filename"] if !ok { return nil, fmt.Errorf("Invalid file upload type %s for %s: no filename", n, prettify(typeDef)) } return &gen.UploadDataType{TypeName: n}, nil } if isBuiltInType(n) { n = "String" } var dataType gen.DataType switch n { case "Integer": i := gen.BasicDataType("int") dataType = &i case "Float": f := gen.BasicDataType("float64") dataType = &f case "String": s := gen.BasicDataType("string") dataType = &s case "Boolean": b := gen.BasicDataType("bool") dataType = &b case "Object": o := gen.BasicDataType("interface{}") dataType = &o case "DateTime": t := gen.BasicDataType("*time.Time") // Need pointer for case where value is null a.descriptor.NeedTime = true dataType = &t case "Collection", "Ids": member, ok := typeDef["member_attribute"].(map[string]interface{}) if !ok { return nil, fmt.Errorf("Missing \"member_attribute\" for %s", prettify(typeDef)) } elemType, err := a.AnalyzeAttribute(n+"Member", query+"[]", member) if err != nil { return nil, fmt.Errorf("Failed to compute type of \"member_attribute\": %s", err) } dataType = &gen.ArrayDataType{elemType} case "Struct": attrs, ok := typeDef["attributes"].(map[string]interface{}) if !ok { return nil, fmt.Errorf("Failed to retrieve attributes of struct for %s", prettify(typeDef)) } obj, err := a.CreateType(query, attrs) if err != nil { return nil, err } dataType = obj case "Hash": keys, ok := typeDef["keys"].(map[string]interface{}) if !ok { dataType = new(gen.EnumerableDataType) } else { obj, err := a.CreateType(query, keys) if err != nil { return nil, err } dataType = obj } default: // First check if we already analyzed that type if t := a.Registry.GetNamedType(n); t != nil { return t, nil } // No then analyze it t, ok := a.RawTypes[n] if !ok { return nil, fmt.Errorf("Unknown type %s for %s", n, prettify(typeDef)) } attrs, ok := t["attributes"] if !ok { // No attribute, it's a string s := gen.BasicDataType("string") dataType = &s } else { att := attrs.(map[string]interface{}) obj := a.Registry.CreateNamedType(n) obj.Fields = make([]*gen.ActionParam, len(att)) for idx, an := range sortedKeys(att) { at := att[an] aq := fmt.Sprintf("%s[%s]", query, an) ap, err := a.AnalyzeAttribute(an, aq, at.(map[string]interface{})) if err != nil { return nil, err } obj.Fields[idx] = ap } // We're done dataType = obj } } return dataType, nil }
analyzer = NewAnalyzer(params) }) Context("with an empty path and a simple param", func() { BeforeEach(func() { path = "" params = map[string]interface{}{"foo": map[string]interface{}{"class": "String"}} }) It("Analyze returns the parsed param", func() { analyzer.Analyze() params := analyzer.Params Ω(params).Should(HaveLen(1)) param := params[0] Ω(param.Name).Should(Equal("foo")) s := gen.BasicDataType("string") Ω(param.Type).Should(BeEquivalentTo(&s)) }) }) Context("with a simple array param", func() { BeforeEach(func() { path = "" params = map[string]interface{}{"foo": map[string]interface{}{"class": "Array"}} }) It("Analyze returns the parsed param", func() { analyzer.Analyze() params := analyzer.Params Ω(params).Should(HaveLen(1)) param := params[0]