// CanonicalParams returns the list of parameter names needed to build the canonical href to the // resource. It returns nil if the resource does not have a canonical action. func CanonicalParams(r *design.ResourceDefinition) []string { var params []string if ca := r.CanonicalAction(); ca != nil { if len(ca.Routes) > 0 { params = ca.Routes[0].Params() } for i, p := range params { params[i] = Goify(p, false) } } return params }
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() }
} route := design.RouteDefinition{ Verb: "GET", Path: "/:id", } at := design.AttributeDefinition{ Type: design.String, } ut := design.UserTypeDefinition{ AttributeDefinition: &at, TypeName: "id", } res := design.ResourceDefinition{ Name: "Widget", BasePath: "/widgets", Description: "Widgetty", MediaType: "vnd.rightscale.codegen.test.widgets", CanonicalActionName: "get", } get := design.ActionDefinition{ Name: "get", Description: "get widgets", Parent: &res, Routes: []*design.RouteDefinition{&route}, Responses: map[string]*design.ResponseDefinition{"ok": &resp}, Params: ¶ms, Payload: payload, } res.Actions = map[string]*design.ActionDefinition{"get": &get} mt := design.MediaTypeDefinition{ UserTypeDefinition: &ut,
// 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 }) }
// CanonicalTemplate returns the resource URI template as a format string suitable for use in the // fmt.Printf function family. func CanonicalTemplate(r *design.ResourceDefinition) string { return design.WildcardRegex.ReplaceAllLiteralString(r.URITemplate(), "/%v") }
}, } action = &design.ActionDefinition{Parent: resource} }) It("does not panic and merges the resource responses", func() { Ω(action.Finalize).ShouldNot(Panic()) Ω(action.Responses).Should(HaveKey("NotFound")) }) }) }) var _ = Describe("FullPath", func() { Context("Given a base resource and a resource with an action with a route", func() { var resource, parentResource *design.ResourceDefinition var action *design.ActionDefinition var route *design.RouteDefinition var actionPath string var resourcePath string var parentResourcePath string JustBeforeEach(func() { showAct := &design.ActionDefinition{} showRoute := &design.RouteDefinition{ Path: parentResourcePath, Parent: showAct, } showAct.Routes = []*design.RouteDefinition{showRoute} parentResource = &design.ResourceDefinition{}
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() }