// 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 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 { ReportError(`duplicate wildcard "%s" in API and resource base paths`, wc) } } } } }
// finalizeResource makes the final pass at the resource DSL. This is needed so that the order // of DSL function calls is irrelevant. For example a resource response may be defined after an // action refers to it. func finalizeResource(r *design.ResourceDefinition) { r.IterateActions(func(a *design.ActionDefinition) error { // 1. Merge response definitions for name, resp := range a.Responses { if pr, ok := a.Parent.Responses[name]; ok { resp.Merge(pr) } if ar, ok := design.Design.Responses[name]; ok { resp.Merge(ar) } if dr, ok := design.Design.DefaultResponses[name]; ok { resp.Merge(dr) } } // 2. Create implicit action parameters for path wildcards that dont' have one for _, r := range a.Routes { design.Design.IterateVersions(func(ver *design.APIVersionDefinition) error { wcs := design.ExtractWildcards(r.FullPath(ver)) for _, wc := range wcs { found := false var o design.Object if all := a.AllParams(); all != nil { o = all.Type.ToObject() } else { o = design.Object{} a.Params = &design.AttributeDefinition{Type: o} } for n := range o { if n == wc { found = true break } } if !found { o[wc] = &design.AttributeDefinition{Type: design.String} } } return nil }) } // 3. Compute QueryParams from Params if params := a.Params; params != nil { queryParams := params.Dup() design.Design.IterateVersions(func(ver *design.APIVersionDefinition) error { for _, route := range a.Routes { pnames := route.Params(ver) for _, pname := range pnames { delete(queryParams.Type.ToObject(), pname) } } return nil }) // (note: we may end up with required attribute names that don't correspond // to actual attributes cos' we just deleted them but that's probably OK.) a.QueryParams = queryParams } return nil }) }
// Routing lists the action route. Each route is defined with a function named after the HTTP method. // The route function takes the path as argument. Route paths may use wildcards as described in the // [httprouter](https://godoc.org/github.com/julienschmidt/httprouter) package documentation. These // wildcards define parameters using the `:name` or `*name` syntax where `:name` matches a path // segment and `*name` is a catch-all that matches the path until the end. func Routing(routes ...*design.RouteDefinition) { if a, ok := actionDefinition(true); ok { for _, r := range routes { rwcs := design.ExtractWildcards(a.Parent.FullPath()) wcs := design.ExtractWildcards(r.Path) for _, rwc := range rwcs { for _, wc := range wcs { if rwc == wc { ReportError(`duplicate wildcard "%s" in resource base path "%s" and action route "%s"`, wc, a.Parent.FullPath(), r.Path) } } } r.Parent = a a.Routes = append(a.Routes, r) } } }
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 }
// finalizeResource makes the final pass at the resource DSL. This is needed so that the order // of DSL function calls is irrelevant. For example a resource response may be defined after an // action refers to it. func finalizeResource(r *design.ResourceDefinition) { r.IterateActions(func(a *design.ActionDefinition) error { // 1. Merge response definitions for name, resp := range a.Responses { if pr, ok := a.Parent.Responses[name]; ok { resp.Merge(pr) } if ar, ok := design.Design.Responses[name]; ok { resp.Merge(ar) } if dr, ok := design.Design.DefaultResponses[name]; ok { resp.Merge(dr) } } // 2. Create implicit action parameters for path wildcards that dont' have one for _, r := range a.Routes { wcs := design.ExtractWildcards(r.FullPath()) for _, wc := range wcs { found := false var o design.Object if a.Params != nil { o = a.Params.Type.ToObject() } else { o = design.Object{} a.Params = &design.AttributeDefinition{Type: o} } for n := range o { if n == wc { found = true break } } if !found { o[wc] = &design.AttributeDefinition{Type: design.String} } } } return nil }) }