Example #1
0
func paramsFromDefinition(params *design.AttributeDefinition, path string) ([]*Parameter, error) {
	if params == nil {
		return nil, nil
	}
	obj := params.Type.ToObject()
	if obj == nil {
		return nil, fmt.Errorf("invalid parameters definition, not an object")
	}
	res := make([]*Parameter, len(obj))
	i := 0
	wildcards := design.ExtractWildcards(path)
	obj.IterateAttributes(func(n string, at *design.AttributeDefinition) error {
		in := "query"
		required := params.IsRequired(n)
		for _, w := range wildcards {
			if n == w {
				in = "path"
				required = true
				break
			}
		}
		param := paramFor(at, n, in, required)
		res[i] = param
		i++
		return nil
	})
	return res, nil
}
Example #2
0
func buildPathFromFileServer(s *Swagger, api *design.APIDefinition, fs *design.FileServerDefinition) error {
	wcs := design.ExtractWildcards(fs.RequestPath)
	var param []*Parameter
	if len(wcs) > 0 {
		param = []*Parameter{{
			In:          "path",
			Name:        wcs[0],
			Description: "Relative file path",
			Required:    true,
			Type:        "string",
		}}
	}

	responses := map[string]*Response{
		"200": {
			Description: "File downloaded",
			Schema:      &genschema.JSONSchema{Type: genschema.JSONFile},
		},
	}
	if len(wcs) > 0 {
		schema := genschema.TypeSchema(api, design.ErrorMedia)
		responses["404"] = &Response{Description: "File not found", Schema: schema}
	}

	operationID := fmt.Sprintf("%s#%s", fs.Parent.Name, fs.RequestPath)
	schemes := api.Schemes

	operation := &Operation{
		Description:  fs.Description,
		Summary:      summaryFromDefinition(fmt.Sprintf("Download %s", fs.FilePath), fs.Metadata),
		ExternalDocs: docsFromDefinition(fs.Docs),
		OperationID:  operationID,
		Parameters:   param,
		Responses:    responses,
		Schemes:      schemes,
	}

	applySecurity(operation, fs.Security)

	key := design.WildcardRegex.ReplaceAllStringFunc(
		fs.RequestPath,
		func(w string) string {
			return fmt.Sprintf("/{%s}", w[2:])
		},
	)
	if key == "" {
		key = "/"
	}
	var path interface{}
	var ok bool
	if path, ok = s.Paths[key]; !ok {
		path = new(Path)
		s.Paths[key] = path
	}
	p := path.(*Path)
	p.Get = operation
	p.Extensions = extensionsFromDefinition(fs.Metadata)

	return nil
}
Example #3
0
// BasePath defines the API base path, i.e. the common path prefix to all the API actions.
// The path may define wildcards (see Routing for a description of the wildcard syntax).
// The corresponding parameters must be described using BaseParams.
func BasePath(val string) {
	if a, ok := apiDefinition(false); ok {
		a.BasePath = val
	} else if v, ok := versionDefinition(false); ok {
		v.BasePath = val
	} else if r, ok := resourceDefinition(true); ok {
		r.BasePath = val
		awcs := design.ExtractWildcards(design.Design.BasePath)
		wcs := design.ExtractWildcards(val)
		for _, awc := range awcs {
			for _, wc := range wcs {
				if awc == wc {
					dslengine.ReportError(`duplicate wildcard "%s" in API and resource base paths`, wc)
				}
			}
		}
	}
}
Example #4
0
File: api.go Project: ajoulie/goa
// BasePath defines the API base path, i.e. the common path prefix to all the API actions.
// The path may define wildcards (see Routing for a description of the wildcard syntax).
// The corresponding parameters must be described using BaseParams.
func BasePath(val string) {
	switch def := dslengine.CurrentDefinition().(type) {
	case *design.APIDefinition:
		def.BasePath = val
	case *design.ResourceDefinition:
		def.BasePath = val
		awcs := design.ExtractWildcards(design.Design.BasePath)
		wcs := design.ExtractWildcards(val)
		for _, awc := range awcs {
			for _, wc := range wcs {
				if awc == wc {
					dslengine.ReportError(`duplicate wildcard "%s" in API and resource base paths`, wc)
				}
			}
		}
	default:
		dslengine.IncompatibleDSL()
	}
}
Example #5
0
func paramsFromDefinition(params *design.AttributeDefinition, path string) ([]*Parameter, error) {
	if params == nil {
		return nil, nil
	}
	obj := params.Type.ToObject()
	if obj == nil {
		return nil, fmt.Errorf("invalid parameters definition, not an object")
	}
	res := make([]*Parameter, len(obj))
	i := 0
	wildcards := design.ExtractWildcards(path)
	obj.IterateAttributes(func(n string, at *design.AttributeDefinition) error {
		in := "query"
		required := params.IsRequired(n)
		for _, w := range wildcards {
			if n == w {
				in = "path"
				required = true
				break
			}
		}
		param := &Parameter{
			Name:        n,
			Default:     at.DefaultValue,
			Description: at.Description,
			Required:    required,
			In:          in,
			Type:        at.Type.Name(),
		}
		var items *Items
		if at.Type.IsArray() {
			items = itemsFromDefinition(at)
		}
		param.Items = items
		initValidations(at, param)
		res[i] = param
		i++
		return nil
	})
	return res, nil
}
Example #6
0
func (g *Generator) generateFileServer(file *codegen.SourceFile, fs *design.FileServerDefinition, funcs template.FuncMap) error {
	var (
		dir string

		fsTmpl = template.Must(template.New("fileserver").Funcs(funcs).Parse(fsTmpl))
		name   = g.fileServerMethod(fs)
		wcs    = design.ExtractWildcards(fs.RequestPath)
		scheme = "http"
	)

	if len(wcs) > 0 {
		dir = "/"
		fileElems := filepath.SplitList(fs.FilePath)
		if len(fileElems) > 1 {
			dir = fileElems[len(fileElems)-2]
		}
	}
	if len(design.Design.Schemes) > 0 {
		scheme = design.Design.Schemes[0]
	}
	requestDir, _ := path.Split(fs.RequestPath)

	data := struct {
		Name            string // Download functionn name
		RequestPath     string // File server request path
		FilePath        string // File server file path
		FileName        string // Filename being download if request path has no wildcard
		DirName         string // Parent directory name if request path has wildcard
		RequestDir      string // Request path without wildcard suffix
		CanonicalScheme string // HTTP scheme
	}{
		Name:            name,
		RequestPath:     fs.RequestPath,
		FilePath:        fs.FilePath,
		FileName:        filepath.Base(fs.FilePath),
		DirName:         dir,
		RequestDir:      requestDir,
		CanonicalScheme: scheme,
	}
	return fsTmpl.Execute(file, data)
}
Example #7
0
File: mux.go Project: DavyC/goa
// PathSelectVersionFunc returns a SelectVersionFunc that uses the given path pattern and param to
// extract the version from the request path. Use the same path pattern given in the DSL to define
// the API base path, e.g. "/api/:api_version".
func PathSelectVersionFunc(pattern, param string) (SelectVersionFunc, error) {
	params := design.ExtractWildcards(pattern)
	index := -1
	for i, p := range params {
		if p == param {
			index = i
			break
		}
	}
	if index == -1 {
		return nil, fmt.Errorf("Mux versioning setup: no param %s in pattern %s", param, pattern)
	}
	rgs := strings.Replace(pattern, ":"+param, `([^/]+)`, 1)
	rg := regexp.MustCompile("^" + rgs)
	return func(req *http.Request) (version string) {
		match := rg.FindStringSubmatch(req.URL.Path)
		if len(match) > 1 {
			version = match[1]
		}
		return
	}, nil
}
Example #8
0
// fileServerMethod returns the name of the client method for downloading assets served by the given
// file server.
// Note: the implementation opts for generating good names rather than names that are guaranteed to
// be unique. This means that the generated code could be potentially incorrect in the rare cases
// where it produces the same names for two different file servers. This should be addressed later
// (when it comes up?) using metadata to let users override the default.
func (g *Generator) fileServerMethod(fs *design.FileServerDefinition) string {
	var (
		suffix string

		wcs      = design.ExtractWildcards(fs.RequestPath)
		reqElems = strings.Split(fs.RequestPath, "/")
	)

	if len(wcs) == 0 {
		suffix = path.Base(fs.RequestPath)
		ext := filepath.Ext(suffix)
		suffix = strings.TrimSuffix(suffix, ext)
		suffix += codegen.Goify(ext, true)
	} else {
		if len(reqElems) == 1 {
			suffix = filepath.Base(fs.RequestPath)
			suffix = suffix[1:] // remove "*" prefix
		} else {
			suffix = reqElems[len(reqElems)-2] // should work most of the time
		}
	}
	return "Download" + codegen.Goify(suffix, true)
}
Example #9
0
		BeforeEach(func() {
			id = "application/json+xml; foo=bar"
		})

		It("canonicalizes it", func() {
			Ω(canonical).Should(Equal("application/json; foo=bar"))
		})
	})
})

var _ = Describe("ExtractWildcards", func() {
	var path string
	var wcs []string

	JustBeforeEach(func() {
		wcs = design.ExtractWildcards(path)
	})

	Context("with a path with no wildcard", func() {
		BeforeEach(func() {
			path = "/foo"
		})

		It("returns the empty slice", func() {
			Ω(wcs).Should(HaveLen(0))
		})
	})

	Context("with a path with wildcards", func() {
		BeforeEach(func() {
			path = "/a/:foo/:bar/b/:baz/c"
Example #10
0
func (g *Generator) generateCommands(commandsFile string, clientPkg string, funcs template.FuncMap) error {
	file, err := codegen.SourceFileFor(commandsFile)
	if err != nil {
		return err
	}

	funcs["defaultRouteParams"] = defaultRouteParams
	funcs["defaultRouteTemplate"] = defaultRouteTemplate
	funcs["joinNames"] = joinNames
	funcs["joinRouteParams"] = joinRouteParams
	funcs["routes"] = routes
	funcs["flagType"] = flagType
	funcs["cmdFieldType"] = cmdFieldTypeString
	funcs["formatExample"] = formatExample
	funcs["shouldAddExample"] = shouldAddExample

	commandTypesTmpl := template.Must(template.New("commandTypes").Funcs(funcs).Parse(commandTypesTmpl))
	commandsTmpl := template.Must(template.New("commands").Funcs(funcs).Parse(commandsTmpl))
	commandsTmplWS := template.Must(template.New("commandsWS").Funcs(funcs).Parse(commandsTmplWS))
	downloadCommandTmpl := template.Must(template.New("download").Funcs(funcs).Parse(downloadCommandTmpl))
	registerTmpl := template.Must(template.New("register").Funcs(funcs).Parse(registerTmpl))

	imports := []*codegen.ImportSpec{
		codegen.SimpleImport("encoding/json"),
		codegen.SimpleImport("fmt"),
		codegen.SimpleImport("log"),
		codegen.SimpleImport("net/url"),
		codegen.SimpleImport("os"),
		codegen.SimpleImport("path"),
		codegen.SimpleImport("path/filepath"),
		codegen.SimpleImport("strings"),
		codegen.SimpleImport("strconv"),
		codegen.SimpleImport("time"),
		codegen.SimpleImport("github.com/goadesign/goa"),
		codegen.SimpleImport("github.com/spf13/cobra"),
		codegen.SimpleImport(clientPkg),
		codegen.SimpleImport("golang.org/x/net/context"),
		codegen.SimpleImport("golang.org/x/net/websocket"),
		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
	}
	if len(g.API.Resources) > 0 {
		imports = append(imports, codegen.NewImport("goaclient", "github.com/goadesign/goa/client"))
	}
	if err := file.WriteHeader("", "cli", imports); err != nil {
		return err
	}
	g.genfiles = append(g.genfiles, commandsFile)

	file.Write([]byte("type (\n"))
	var fs []*design.FileServerDefinition
	if err := g.API.IterateResources(func(res *design.ResourceDefinition) error {
		fs = append(fs, res.FileServers...)
		return res.IterateActions(func(action *design.ActionDefinition) error {
			return commandTypesTmpl.Execute(file, action)
		})
	}); err != nil {
		return err
	}
	if len(fs) > 0 {
		file.Write([]byte(downloadCommandType))
	}
	file.Write([]byte(")\n\n"))

	actions := make(map[string][]*design.ActionDefinition)
	hasDownloads := false
	g.API.IterateResources(func(res *design.ResourceDefinition) error {
		if len(res.FileServers) > 0 {
			hasDownloads = true
		}
		return res.IterateActions(func(action *design.ActionDefinition) error {
			name := codegen.Goify(action.Name, false)
			if as, ok := actions[name]; ok {
				actions[name] = append(as, action)
			} else {
				actions[name] = []*design.ActionDefinition{action}
			}
			return nil
		})
	})
	data := struct {
		Actions      map[string][]*design.ActionDefinition
		Package      string
		HasDownloads bool
	}{
		Actions:      actions,
		Package:      g.Target,
		HasDownloads: hasDownloads,
	}
	if err := file.ExecuteTemplate("registerCmds", registerCmdsT, funcs, data); err != nil {
		return err
	}

	var fsdata []map[string]interface{}
	g.API.IterateResources(func(res *design.ResourceDefinition) error {
		if res.FileServers != nil {
			res.IterateFileServers(func(fs *design.FileServerDefinition) error {
				wcs := design.ExtractWildcards(fs.RequestPath)
				isDir := len(wcs) > 0
				var reqDir, filename string
				if isDir {
					reqDir, _ = path.Split(fs.RequestPath)
				} else {
					_, filename = filepath.Split(fs.FilePath)
				}
				fsdata = append(fsdata, map[string]interface{}{
					"IsDir":       isDir,
					"RequestPath": fs.RequestPath,
					"FilePath":    fs.FilePath,
					"FileName":    filename,
					"Name":        g.fileServerMethod(fs),
					"RequestDir":  reqDir,
				})
				return nil
			})
		}
		return nil
	})
	if fsdata != nil {
		data := struct {
			Package     string
			FileServers []map[string]interface{}
		}{
			Package:     g.Target,
			FileServers: fsdata,
		}
		if err := downloadCommandTmpl.Execute(file, data); err != nil {
			return err
		}
	}
	err = g.API.IterateResources(func(res *design.ResourceDefinition) error {
		return res.IterateActions(func(action *design.ActionDefinition) error {
			data := map[string]interface{}{
				"Action":          action,
				"Resource":        action.Parent,
				"Package":         g.Target,
				"HasMultiContent": len(g.API.Consumes) > 1,
			}
			var err error
			if action.WebSocket() {
				err = commandsTmplWS.Execute(file, data)
			} else {
				err = commandsTmpl.Execute(file, data)

			}
			if err != nil {
				return err
			}
			err = registerTmpl.Execute(file, data)
			return err
		})
	})
	if err != nil {
		return err
	}

	return file.FormatCode()
}