Example #1
0
// 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
}
Example #2
0
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()
}
Example #3
0
			}
			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:      &params,
				Payload:     payload,
			}
			res.Actions = map[string]*design.ActionDefinition{"get": &get}
			mt := design.MediaTypeDefinition{
				UserTypeDefinition: &ut,
Example #4
0
// 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
	})
}
Example #5
0
// 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{}
Example #7
0
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()
}