// Headers implements the DSL for describing HTTP headers. The DSL syntax is identical to the one // of Attribute. Here is an example defining a couple of headers with validations: // // Headers(func() { // Header("Authorization") // Header("X-Account", Integer, func() { // Minimum(1) // }) // Required("Authorization") // }) // // Headers can be used inside Action to define the action request headers, Response to define the // response headers or Resource to define common request headers to all the resource actions. func Headers(params ...interface{}) { if len(params) == 0 { dslengine.ReportError("missing parameter") return } dsl, ok := params[0].(func()) if ok { switch def := dslengine.CurrentDefinition().(type) { case *design.ActionDefinition: headers := newAttribute(def.Parent.MediaType) if dslengine.Execute(dsl, headers) { def.Headers = headers } case *design.ResourceDefinition: headers := newAttribute(def.MediaType) if dslengine.Execute(dsl, headers) { def.Headers = headers } case *design.ResponseDefinition: if def.Headers != nil { dslengine.ReportError("headers already defined") return } var h *design.AttributeDefinition switch actual := def.Parent.(type) { case *design.ResourceDefinition: h = newAttribute(actual.MediaType) case *design.ActionDefinition: h = newAttribute(actual.Parent.MediaType) case nil: // API ResponseTemplate h = &design.AttributeDefinition{} default: dslengine.ReportError("invalid use of Response or ResponseTemplate") } if dslengine.Execute(dsl, h) { def.Headers = h } default: dslengine.IncompatibleDSL() } } else if cors, ok := corsDefinition(); ok { vals := make([]string, len(params)) for i, p := range params { if v, ok := p.(string); ok { vals[i] = v } else { dslengine.ReportError("invalid parameter at position %d: must be a string", i) return } } cors.Headers = vals } else { dslengine.IncompatibleDSL() } }
// responseDefinition returns true and current context if it is a ResponseDefinition, // nil and false otherwise. func responseDefinition(failIfNotResponse bool) (*design.ResponseDefinition, bool) { r, ok := dslengine.CurrentDefinition().(*design.ResponseDefinition) if !ok && failIfNotResponse { dslengine.IncompatibleDSL(dslengine.Caller()) } return r, ok }
// actionDefinition returns true and current context if it is an ActionDefinition, // nil and false otherwise. func actionDefinition(failIfNotAction bool) (*design.ActionDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*design.ActionDefinition) if !ok && failIfNotAction { dslengine.IncompatibleDSL(dslengine.Caller()) } return a, ok }
// typeDefinition returns true and current context if it is a UserTypeDefinition, // nil and false otherwise. func typeDefinition(failIfNotMT bool) (*design.UserTypeDefinition, bool) { m, ok := dslengine.CurrentDefinition().(*design.UserTypeDefinition) if !ok && failIfNotMT { dslengine.IncompatibleDSL(dslengine.Caller()) } return m, ok }
// docsDefinition returns true and current context if it is a DocsDefinition, // nil and false otherwise. func docsDefinition(failIfNotDocs bool) (*design.DocsDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*design.DocsDefinition) if !ok && failIfNotDocs { dslengine.IncompatibleDSL(dslengine.Caller()) } return a, ok }
// licenseDefinition returns true and current context if it is an APIDefinition, // nil and false otherwise. func licenseDefinition(failIfNotLicense bool) (*design.LicenseDefinition, bool) { l, ok := dslengine.CurrentDefinition().(*design.LicenseDefinition) if !ok && failIfNotLicense { dslengine.IncompatibleDSL(dslengine.Caller()) } return l, ok }
// Scope defines an authorization scope. Used within SecurityScheme, a description may be provided // explaining what the scope means. Within a Security block, only a scope is needed. func Scope(name string, desc ...string) { switch current := dslengine.CurrentDefinition().(type) { case *design.SecurityDefinition: if len(desc) >= 1 { dslengine.ReportError("too many arguments") return } current.Scopes = append(current.Scopes, name) case *design.SecuritySchemeDefinition: if len(desc) > 1 { dslengine.ReportError("too many arguments") return } if current.Scopes == nil { current.Scopes = make(map[string]string) } d := "no description" if len(desc) == 1 { d = desc[0] } current.Scopes[name] = d default: dslengine.IncompatibleDSL() } }
// buildSourceDefinition returns true and current context if it is an BuildSource // nil and false otherwise. func buildSourceDefinition(failIfNotSD bool) (*gorma.BuildSource, bool) { a, ok := dslengine.CurrentDefinition().(*gorma.BuildSource) if !ok && failIfNotSD { dslengine.IncompatibleDSL() } return a, ok }
// responseDefinition returns true and current context if it is a ResponseDefinition, // nil and false otherwise. func responseDefinition() (*design.ResponseDefinition, bool) { r, ok := dslengine.CurrentDefinition().(*design.ResponseDefinition) if !ok { dslengine.IncompatibleDSL() } return r, ok }
// Metadata is a set of key/value pairs that can be assigned to an object. Each value consists of a // slice of strings so that multiple invocation of the Metadata function on the same target using // the same key builds up the slice. Metadata may be set on attributes, media types, actions, // responses, resources and API definitions. // // While keys can have any value the following names are handled explicitly by goagen when set on // attributes. // // `struct:field:name`: overrides the Go struct field name generated by default by goagen. // Applicable to attributes only. // // Metadata("struct:field:name", "MyName") // // `struct:tag:xxx`: sets the struct field tag xxx on generated Go structs. Overrides tags that // goagen would otherwise set. If the metadata value is a slice then the strings are joined with // the space character as separator. // Applicable to attributes only. // // Metadata("struct:tag:json", "myName,omitempty") // Metadata("struct:tag:xml", "myName,attr") // // `swagger:generate`: specifies whether Swagger specification should be generated. Defaults to // true. // Applicable to resources, actions and file servers. // // Metadata("swagger:generate", "false") // // `swagger:summary`: sets the Swagger operation summary field. // Applicable to actions. // // Metadata("swagger:summary", "Short summary of what action does") // // `swagger:tag:xxx`: sets the Swagger object field tag xxx. // Applicable to resources and actions. // // Metadata("swagger:tag:Backend") // Metadata("swagger:tag:Backend:desc", "Quick description of what 'Backend' is") // Metadata("swagger:tag:Backend:url", "http://example.com") // Metadata("swagger:tag:Backend:url:desc", "See more docs here") // // `swagger:extension:xxx`: sets the Swagger extensions xxx. It can have any valid JSON format value. // Applicable to // api as within the info and tag object, // resource as within the paths object, // action as within the path-item object, // route as within the operation object, // param as within the parameter object, // response as within the response object // and security as within the security-scheme object. // See https://github.com/OAI/OpenAPI-Specification/blob/master/guidelines/EXTENSIONS.md. // // Metadata("swagger:extension:x-api", `{"foo":"bar"}`) // // The special key names listed above may be used as follows: // // var Account = Type("Account", func() { // Attribute("service", String, "Name of service", func() { // // Override default name to avoid clash with built-in 'Service' field. // Metadata("struct:field:name", "ServiceName") // }) // }) // func Metadata(name string, value ...string) { appendMetadata := func(metadata dslengine.MetadataDefinition, name string, value ...string) dslengine.MetadataDefinition { if metadata == nil { metadata = make(map[string][]string) } metadata[name] = append(metadata[name], value...) return metadata } switch def := dslengine.CurrentDefinition().(type) { case design.ContainerDefinition: att := def.Attribute() att.Metadata = appendMetadata(att.Metadata, name, value...) case *design.AttributeDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.MediaTypeDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.ActionDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.FileServerDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.ResourceDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.ResponseDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.APIDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.RouteDefinition: def.Metadata = appendMetadata(def.Metadata, name, value...) case *design.SecurityDefinition: def.Scheme.Metadata = appendMetadata(def.Scheme.Metadata, name, value...) default: dslengine.IncompatibleDSL() } }
// actionDefinition returns true and current context if it is an ActionDefinition, // nil and false otherwise. func actionDefinition() (*design.ActionDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*design.ActionDefinition) if !ok { dslengine.IncompatibleDSL() } return a, ok }
// corsDefinition returns true and current context if it is a CORSDefinition, nil And // false otherwise. func corsDefinition() (*design.CORSDefinition, bool) { cors, ok := dslengine.CurrentDefinition().(*design.CORSDefinition) if !ok { dslengine.IncompatibleDSL() } return cors, ok }
// typeDefinition returns true and current context if it is a UserTypeDefinition, // nil and false otherwise. func typeDefinition() (*design.UserTypeDefinition, bool) { m, ok := dslengine.CurrentDefinition().(*design.UserTypeDefinition) if !ok { dslengine.IncompatibleDSL() } return m, ok }
// licenseDefinition returns true and current context if it is an APIDefinition, // nil and false otherwise. func licenseDefinition() (*design.LicenseDefinition, bool) { l, ok := dslengine.CurrentDefinition().(*design.LicenseDefinition) if !ok { dslengine.IncompatibleDSL() } return l, ok }
// encodingDefinition returns true and current context if it is an EncodingDefinition, // nil and false otherwise. func encodingDefinition(failIfNotEnc bool) (*design.EncodingDefinition, bool) { e, ok := dslengine.CurrentDefinition().(*design.EncodingDefinition) if !ok && failIfNotEnc { dslengine.IncompatibleDSL(dslengine.Caller()) } return e, ok }
// relationalFieldDefinition returns true and current context if it is an RelationalFieldDefinition, // nil and false otherwise. func relationalFieldDefinition(failIfNotSD bool) (*gorma.RelationalFieldDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*gorma.RelationalFieldDefinition) if !ok && failIfNotSD { dslengine.IncompatibleDSL() } return a, ok }
// contactDefinition returns true and current context if it is an ContactDefinition, // nil and false otherwise. func contactDefinition(failIfNotContact bool) (*design.ContactDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*design.ContactDefinition) if !ok && failIfNotContact { dslengine.IncompatibleDSL(dslengine.Caller()) } return a, ok }
// attributeDefinition returns true and current context if it is an AttributeDefinition // nil and false otherwise. func attributeDefinition(failIfNotSD bool) (*design.AttributeDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*design.AttributeDefinition) if !ok && failIfNotSD { dslengine.IncompatibleDSL() } return a, ok }
// storageDefinition returns true and current context if it is an StorageGroupDefinition, // nil and false otherwise. func storageGroupDefinition(failIfNotSD bool) (*gorma.StorageGroupDefinition, bool) { a, ok := dslengine.CurrentDefinition().(*gorma.StorageGroupDefinition) if !ok && failIfNotSD { dslengine.IncompatibleDSL() } return a, ok }
// encodingDefinition returns true and current context if it is an EncodingDefinition, // nil and false otherwise. func encodingDefinition() (*design.EncodingDefinition, bool) { e, ok := dslengine.CurrentDefinition().(*design.EncodingDefinition) if !ok { dslengine.IncompatibleDSL() } return e, ok }
// Params describe the action parameters, either path parameters identified via wildcards or query // string parameters if there is no corresponding path parameter. Each parameter is described via // the Param function which uses the same DSL as the Attribute DSL. Here is an example: // // Params(func() { // Param("id", Integer) // A path parameter defined using e.g. GET("/:id") // Param("sort", String, func() { // A query string parameter // Enum("asc", "desc") // }) // }) // // Params can be used inside Action to define the action parameters, Resource to define common // parameters to all the resource actions or API to define common parameters to all the API actions. // // If Params is used inside Resource or Action then the resource base media type attributes provide // default values for all the properties of params with identical names. For example: // // var BottleMedia = MediaType("application/vnd.bottle", func() { // Attributes(func() { // Attribute("name", String, "The name of the bottle", func() { // MinLength(2) // BottleMedia has one attribute "name" which is a // // string that must be at least 2 characters long. // }) // }) // View("default", func() { // Attribute("name") // }) // }) // // var _ = Resource("Bottle", func() { // DefaultMedia(BottleMedia) // Resource "Bottle" uses "BottleMedia" as default // Action("show", func() { // media type. // Routing(GET("/:name")) // Params(func() { // Param("name") // inherits type, description and validation from // // BottleMedia "name" attribute // }) // }) // }) // func Params(dsl func()) { var params *design.AttributeDefinition switch def := dslengine.CurrentDefinition().(type) { case *design.ActionDefinition: params = newAttribute(def.Parent.MediaType) case *design.ResourceDefinition: params = newAttribute(def.MediaType) case *design.APIDefinition: params = new(design.AttributeDefinition) default: dslengine.IncompatibleDSL() } params.Type = make(design.Object) if !dslengine.Execute(dsl, params) { return } switch def := dslengine.CurrentDefinition().(type) { case *design.ActionDefinition: def.Params = def.Params.Merge(params) // Useful for traits case *design.ResourceDefinition: def.Params = def.Params.Merge(params) // Useful for traits case *design.APIDefinition: def.Params = def.Params.Merge(params) // Useful for traits } }
// UseTrait executes the API trait with the given name. UseTrait can be used inside a Resource, // Action, Type, MediaType or Attribute DSL. UseTrait takes a variable number // of trait names. func UseTrait(names ...string) { var def dslengine.Definition switch typedDef := dslengine.CurrentDefinition().(type) { case *design.ResourceDefinition: def = typedDef case *design.ActionDefinition: def = typedDef case *design.AttributeDefinition: def = typedDef case *design.MediaTypeDefinition: def = typedDef default: dslengine.IncompatibleDSL() } if def != nil { for _, name := range names { if trait, ok := design.Design.Traits[name]; ok { dslengine.Execute(trait.DSLFunc, def) } else { dslengine.ReportError("unknown trait %s", name) } } } }
// Origin defines the CORS policy for a given origin. The origin can use a wildcard prefix // such as "https://*.mydomain.com". The special value "*" defines the policy for all origins // (in which case there should be only one Origin DSL in the parent resource). // The origin can also be a regular expression wrapped into "/". // Example: // // Origin("http://swagger.goa.design", func() { // Define CORS policy, may be prefixed with "*" wildcard // Headers("X-Shared-Secret") // One or more authorized headers, use "*" to authorize all // Methods("GET", "POST") // One or more authorized HTTP methods // Expose("X-Time") // One or more headers exposed to clients // MaxAge(600) // How long to cache a preflight request response // Credentials() // Sets Access-Control-Allow-Credentials header // }) // // Origin("/[api|swagger].goa.design/", func() {}) // Define CORS policy with a regular expression func Origin(origin string, dsl func()) { cors := &design.CORSDefinition{Origin: origin} if strings.HasPrefix(origin, "/") && strings.HasSuffix(origin, "/") { cors.Regexp = true cors.Origin = strings.Trim(origin, "/") } if !dslengine.Execute(dsl, cors) { return } var parent dslengine.Definition switch def := dslengine.CurrentDefinition().(type) { case *design.APIDefinition: parent = def if def.Origins == nil { def.Origins = make(map[string]*design.CORSDefinition) } def.Origins[origin] = cors case *design.ResourceDefinition: parent = def if def.Origins == nil { def.Origins = make(map[string]*design.CORSDefinition) } def.Origins[origin] = cors default: dslengine.IncompatibleDSL() return } cors.Parent = parent }
// BasicAuthSecurity defines a "basic" security scheme for the API. // // Example: // // BasicAuthSecurity("password", func() { // Description("Use your own password!") // }) // func BasicAuthSecurity(name string, dsl ...func()) *design.SecuritySchemeDefinition { switch dslengine.CurrentDefinition().(type) { case *design.APIDefinition, *dslengine.TopLevelDefinition: default: dslengine.IncompatibleDSL() return nil } if securitySchemeRedefined(name) { return nil } def := &design.SecuritySchemeDefinition{ Kind: design.BasicAuthSecurityKind, SchemeName: name, Type: "basic", } if len(dsl) != 0 { def.DSLFunc = dsl[0] } design.Design.SecuritySchemes = append(design.Design.SecuritySchemes, def) return def }
// TokenURL defines a URL to get an access token. If you are defining OAuth2 flows, use // `ImplicitFlow`, `PasswordFlow`, `AccessCodeFlow` or `ApplicationFlow` instead. This will set an // endpoint where you can obtain a JWT with the JWTSecurity scheme. The URL may be a complete URL // or just a path in which case the API scheme and host are used to build the full URL. func TokenURL(tokenURL string) { if parent, ok := dslengine.CurrentDefinition().(*design.SecuritySchemeDefinition); ok { if parent.Kind == design.JWTSecurityKind { parent.TokenURL = tokenURL return } } dslengine.IncompatibleDSL() }
// NoExample sets the example of an attribute to be blank for the documentation. It is used when // users don't want any custom or auto-generated example func NoExample() { switch def := dslengine.CurrentDefinition().(type) { case *design.APIDefinition: def.NoExamples = true case *design.AttributeDefinition: def.SetExample(nil) default: dslengine.IncompatibleDSL() } }
// Name sets the contact or license name. func Name(name string) { switch def := dslengine.CurrentDefinition().(type) { case *design.ContactDefinition: def.Name = name case *design.LicenseDefinition: def.Name = name default: dslengine.IncompatibleDSL() } }
// TypeName makes it possible to set the Go struct name for a type or media type in the generated // code. By default goagen uses the name (type) or identifier (media type) given in the apidsl and // computes a valid Go identifier from it. This function makes it possible to override that and // provide a custom name. name must be a valid Go identifier. func TypeName(name string) { switch def := dslengine.CurrentDefinition().(type) { case *design.MediaTypeDefinition: def.TypeName = name case *design.UserTypeDefinition: def.TypeName = name default: dslengine.IncompatibleDSL() } }
// Reference sets a type or media type reference. The value itself can be a type or a media type. // The reference type attributes define the default properties for attributes with the same name in // the type using the reference. So for example if a type is defined as such: // // var Bottle = Type("bottle", func() { // Attribute("name", func() { // MinLength(3) // }) // Attribute("vintage", Integer, func() { // Minimum(1970) // }) // Attribute("somethingelse") // }) // // Declaring the following media type: // // var BottleMedia = MediaType("vnd.goa.bottle", func() { // Reference(Bottle) // Attributes(func() { // Attribute("id", Integer) // Attribute("name") // Attribute("vintage") // }) // }) // // defines the "name" and "vintage" attributes with the same type and validations as defined in // the Bottle type. func Reference(t design.DataType) { switch def := dslengine.CurrentDefinition().(type) { case *design.MediaTypeDefinition: def.Reference = t case *design.AttributeDefinition: def.Reference = t default: dslengine.IncompatibleDSL() } }
// ImplicitFlow defines an "implicit" OAuth2 flow. Use within an OAuth2Security definition. func ImplicitFlow(authorizationURL string) { if parent, ok := dslengine.CurrentDefinition().(*design.SecuritySchemeDefinition); ok { if parent.Kind == design.OAuth2SecurityKind { parent.Flow = "implicit" parent.AuthorizationURL = authorizationURL return } } dslengine.IncompatibleDSL() }