// GenerateResourceDefinition produces the JSON schema corresponding to the given API resource. // It stores the results in cachedSchema. func GenerateResourceDefinition(api *design.APIDefinition, r *design.ResourceDefinition) { s := NewJSONSchema() s.Description = r.Description s.Type = JSONObject s.Title = r.Name Definitions[r.Name] = s if mt, ok := api.MediaTypes[r.MediaType]; ok { for _, v := range mt.Views { buildMediaTypeSchema(api, mt, v.Name, s) } } r.IterateActions(func(a *design.ActionDefinition) error { var requestSchema *JSONSchema if a.Payload != nil { requestSchema = TypeSchema(api, a.Payload) requestSchema.Description = a.Name + " payload" } if a.Params != nil { params := design.DupAtt(a.Params) // We don't want to keep the path params, these are defined inline in the href for _, r := range a.Routes { for _, p := range r.Params() { delete(params.Type.ToObject(), p) } } } var targetSchema *JSONSchema var identifier string for _, resp := range a.Responses { if mt, ok := api.MediaTypes[resp.MediaType]; ok { if identifier == "" { identifier = mt.Identifier } else { identifier = "" } if targetSchema == nil { targetSchema = TypeSchema(api, mt) } else if targetSchema.AnyOf == nil { firstSchema := targetSchema targetSchema = NewJSONSchema() targetSchema.AnyOf = []*JSONSchema{firstSchema, TypeSchema(api, mt)} } else { targetSchema.AnyOf = append(targetSchema.AnyOf, TypeSchema(api, mt)) } } } for i, r := range a.Routes { link := JSONLink{ Title: a.Name, Rel: a.Name, Href: toSchemaHref(api, r), Method: r.Verb, Schema: requestSchema, TargetSchema: targetSchema, MediaType: identifier, } if i == 0 { if ca := a.Parent.CanonicalAction(); ca != nil { if ca.Name == a.Name { link.Rel = "self" } } } s.Links = append(s.Links, &link) } return nil }) }
func (g *Generator) generateResourceClient(res *design.ResourceDefinition, funcs template.FuncMap) error { payloadTmpl := template.Must(template.New("payload").Funcs(funcs).Parse(payloadTmpl)) clientsTmpl := template.Must(template.New("clients").Funcs(funcs).Parse(clientsTmpl)) clientsWSTmpl := template.Must(template.New("clients").Funcs(funcs).Parse(clientsWSTmpl)) pathTmpl := template.Must(template.New("pathTemplate").Funcs(funcs).Parse(pathTmpl)) resFilename := codegen.SnakeCase(res.Name) if resFilename == typesFileName { // Avoid clash with datatypes.go resFilename += "_client" } filename := filepath.Join(codegen.OutputDir, resFilename+".go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("bytes"), codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("net/http"), codegen.SimpleImport("net/url"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), codegen.NewImport("uuid", "github.com/satori/go.uuid"), } if err := file.WriteHeader("", "client", imports); err != nil { return err } g.genfiles = append(g.genfiles, filename) g.generatedTypes = make(map[string]bool) err = res.IterateActions(func(action *design.ActionDefinition) error { if action.Payload != nil { if err := payloadTmpl.Execute(file, action); err != nil { return err } g.generatedTypes[action.Payload.TypeName] = true } if action.Params != nil { params := make(design.Object, len(action.QueryParams.Type.ToObject())) for n, param := range action.QueryParams.Type.ToObject() { name := codegen.Goify(n, false) params[name] = param } action.QueryParams.Type = params } if action.Headers != nil { headers := make(design.Object, len(action.Headers.Type.ToObject())) for n, header := range action.Headers.Type.ToObject() { name := codegen.Goify(n, false) headers[name] = header } action.Headers.Type = headers } if action.WebSocket() { return clientsWSTmpl.Execute(file, action) } for i, r := range action.Routes { data := struct { Route *design.RouteDefinition Index int }{ Route: r, Index: i, } if err := pathTmpl.Execute(file, data); err != nil { return err } } return clientsTmpl.Execute(file, action) }) if err != nil { return err } return file.FormatCode() }
func (g *Generator) generateResourceClient(pkgDir string, res *design.ResourceDefinition, funcs template.FuncMap) error { payloadTmpl := template.Must(template.New("payload").Funcs(funcs).Parse(payloadTmpl)) pathTmpl := template.Must(template.New("pathTemplate").Funcs(funcs).Parse(pathTmpl)) resFilename := codegen.SnakeCase(res.Name) if resFilename == typesFileName { // Avoid clash with datatypes.go resFilename += "_client" } filename := filepath.Join(pkgDir, resFilename+".go") file, err := codegen.SourceFileFor(filename) if err != nil { return err } imports := []*codegen.ImportSpec{ codegen.SimpleImport("bytes"), codegen.SimpleImport("encoding/json"), codegen.SimpleImport("fmt"), codegen.SimpleImport("io"), codegen.SimpleImport("io/ioutil"), codegen.SimpleImport("net/http"), codegen.SimpleImport("net/url"), codegen.SimpleImport("os"), codegen.SimpleImport("path"), codegen.SimpleImport("strconv"), codegen.SimpleImport("strings"), codegen.SimpleImport("time"), codegen.SimpleImport("golang.org/x/net/context"), codegen.SimpleImport("golang.org/x/net/websocket"), codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"), } if err := file.WriteHeader("", g.Target, imports); err != nil { return err } g.genfiles = append(g.genfiles, filename) err = res.IterateFileServers(func(fs *design.FileServerDefinition) error { return g.generateFileServer(file, fs, funcs) }) err = res.IterateActions(func(action *design.ActionDefinition) error { if action.Payload != nil { found := false typeName := action.Payload.TypeName for _, t := range design.Design.Types { if t.TypeName == typeName { found = true break } } if !found { if err := payloadTmpl.Execute(file, action); err != nil { return err } } } for i, r := range action.Routes { data := struct { Route *design.RouteDefinition Index int }{ Route: r, Index: i, } if err := pathTmpl.Execute(file, data); err != nil { return err } } return g.generateActionClient(action, file, funcs) }) if err != nil { return err } return file.FormatCode() }