// 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 }) }
// 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 }) }
} 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, } res.Actions = map[string]*design.ActionDefinition{"get": &get} mt := design.MediaTypeDefinition{ UserTypeDefinition: &ut, Identifier: "vnd.rightscale.codegen.test.widgets",
// 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 { buildMediaTypeSchema(api, mt, s) } for _, a := range r.Actions { var requestSchema *JSONSchema if a.Payload != nil { requestSchema = TypeSchema(api, a.Payload) requestSchema.Description = a.Name + " payload" } if a.Params != nil { params := a.Params.Dup() // 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) } } }