Пример #1
0
// MediaType implements the media type definition DSL. A media type definition describes the
// representation of a resource used in a response body.
//
// Media types are defined with a unique identifier as defined by RFC6838. The identifier also
// defines the default value for the Content-Type header of responses. The ContentType DSL allows
// overridding the default as shown in the example below.
//
// The media type definition includes a listing of all the potential attributes that can appear in
// the body. Views specify which of the attributes are actually rendered so that the same media type
// definition may represent multiple rendering of a given resource representation.
//
// All media types must define a view named "default". This view is used to render the media type in
// response bodies when no other view is specified.
//
// A media type definition may also define links to other media types. This is done by first
// defining an attribute for the linked-to media type and then referring to that attribute in the
// Links DSL. Views may then elect to render one or the other or both. Links are rendered using the
// special "link" view. Media types that are linked to must define that view. Here is an example
// showing all the possible media type sub-definitions:
//
//    MediaType("application/vnd.goa.example.bottle", func() {
//        Description("A bottle of wine")
//        TypeName("BottleMedia")         // Override default generated name
//        ContentType("application/json") // Override default Content-Type header value
//        Attributes(func() {
//            Attribute("id", Integer, "ID of bottle")
//            Attribute("href", String, "API href of bottle")
//            Attribute("account", Account, "Owner account")
//            Attribute("origin", Origin, "Details on wine origin")
//            Links(func() {
//                Link("account")         // Defines link to Account media type
//                Link("origin", "tiny")  // Set view used to render link if not "link"
//            })
//            Required("id", "href")
//        })
//        View("default", func() {
//            Attribute("id")
//            Attribute("href")
//            Attribute("links")          // Renders links
//        })
//        View("extended", func() {
//            Attribute("id")
//            Attribute("href")
//            Attribute("account")        // Renders account inline
//            Attribute("origin")         // Renders origin inline
//            Attribute("links")          // Renders links
//        })
//     })
//
// This function returns the media type definition so it can be referred to throughout the apidsl.
func MediaType(identifier string, apidsl func()) *design.MediaTypeDefinition {
	if design.Design.MediaTypes == nil {
		design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition)
	}

	if !dslengine.IsTopLevelDefinition() {
		dslengine.IncompatibleDSL()
		return nil
	}

	// Validate Media Type
	identifier, params, err := mime.ParseMediaType(identifier)
	if err != nil {
		dslengine.ReportError("invalid media type identifier %#v: %s",
			identifier, err)
		// We don't return so that other errors may be
		// captured in this one run.
		identifier = "text/plain"
	}
	canonicalID := design.CanonicalIdentifier(identifier)
	// Validate that media type identifier doesn't clash
	if _, ok := design.Design.MediaTypes[canonicalID]; ok {
		dslengine.ReportError("media type %#v with canonical identifier %#v is defined twice", identifier, canonicalID)
		return nil
	}
	identifier = mime.FormatMediaType(identifier, params)
	lastPart := identifier
	lastPartIndex := strings.LastIndex(identifier, "/")
	if lastPartIndex > -1 {
		lastPart = identifier[lastPartIndex+1:]
	}
	plusIndex := strings.Index(lastPart, "+")
	if plusIndex > 0 {
		lastPart = lastPart[:plusIndex]
	}
	lastPart = strings.TrimPrefix(lastPart, "vnd.")
	elems := strings.Split(lastPart, ".")
	for i, e := range elems {
		elems[i] = strings.Title(e)
	}
	typeName := strings.Join(elems, "")
	if typeName == "" {
		mediaTypeCount++
		typeName = fmt.Sprintf("MediaType%d", mediaTypeCount)
	}
	// Now save the type in the API media types map
	mt := design.NewMediaTypeDefinition(typeName, identifier, apidsl)
	design.Design.MediaTypes[canonicalID] = mt
	return mt
}
Пример #2
0
// Resource implements the resource definition dsl. There is one resource definition per resource
// exposed by the API. The resource dsl allows setting the resource default media type. This media
// type is used to render the response body of actions that return the OK response (unless the
// action overrides the default). The default media type also sets the properties of the request
// payload attributes with the same name. See DefaultMedia.
//
// The resource dsl also allows listing the supported resource collection and resource collection
// item actions. Each action corresponds to a specific API endpoint. See Action.
//
// The resource dsl can also specify a parent resource. Parent resources have two effects.
// First, they set the prefix of all resource action paths to the parent resource href. Note that
// actions can override the path using an absolute path (that is a path starting with "//").
// Second, goa uses the parent resource href coupled with the resource BasePath if any to build
// hrefs to the resource collection or resource collection items. By default goa uses the show
// action if present to compute a resource href (basically concatenating the parent resource href
// with the base path and show action path). The resource definition may specify a canonical action
// via CanonicalActionName to override that default. Here is an example of a resource definition:
//
//	Resource("bottle", func() {
//		Description("A wine bottle")	// Resource description
//		DefaultMedia(BottleMedia)	// Resource default media type
//		BasePath("/bottles")		// Common resource action path prefix if not ""
//		Parent("account")		// Name of parent resource if any
//		CanonicalActionName("get")	// Name of action that returns canonical representation if not "show"
//		UseTrait("Authenticated")	// Included trait if any, can appear more than once
//
//		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
//		})
//
//		Response(Unauthorized, ErrorMedia) // Common responses to all actions
//		Response(BadRequest, ErrorMedia)
//
//		Action("show", func() {		// Action definition, can appear more than once
//			// ... Action dsl
//		})
//	})
func Resource(name string, dsl func()) *design.ResourceDefinition {
	if design.Design.Resources == nil {
		design.Design.Resources = make(map[string]*design.ResourceDefinition)
	}
	if !dslengine.IsTopLevelDefinition() {
		dslengine.IncompatibleDSL()
		return nil
	}

	if _, ok := design.Design.Resources[name]; ok {
		dslengine.ReportError("resource %#v is defined twice", name)
		return nil
	}
	resource := design.NewResourceDefinition(name, dsl)
	design.Design.Resources[name] = resource
	return resource
}
Пример #3
0
// API implements the top level API DSL. It defines the API name, default description and other
// default global property values. Here is an example showing all the possible API sub-definitions:
//
//	API("API name", func() {
//		Title("title")				// API title used in documentation
//		Description("description")		// API description used in documentation
//		Version("2.0")				// API version being described
//		TermsOfService("terms")
//		Contact(func() {			// API Contact information
//			Name("contact name")
//			Email("contact email")
//			URL("contact URL")
//		})
//		License(func() {			// API Licensing information
//			Name("license name")
//			URL("license URL")
//		})
//	 	Docs(func() {
//			Description("doc description")
//			URL("doc URL")
//		})
//		Host("goa.design")			// API hostname
//		Scheme("http")
//		BasePath("/base/:param")		// Common base path to all API actions
//		Params(func() {				// Common parameters to all API actions
//			Param("param")
//		})
//		Security("JWT")
//		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
//		})
//		Consumes("application/xml") // Built-in encoders and decoders
//		Consumes("application/json")
//		Produces("application/gob")
//		Produces("application/json", func() {   // Custom encoder
//			Package("github.com/goadesign/goa/encoding/json")
//		})
//		ResponseTemplate("static", func() {	// Response template for use by actions
//			Description("description")
//			Status(404)
//			MediaType("application/json")
//		})
//		ResponseTemplate("dynamic", func(arg1, arg2 string) {
//			Description(arg1)
//			Status(200)
//			MediaType(arg2)
//		})
//              NoExample()                             // Prevent automatic generation of examples
//		Trait("Authenticated", func() {		// Traits define DSL that can be run anywhere
//			Headers(func() {
//				Header("header")
//				Required("header")
//			})
//		})
//	}
//
func API(name string, dsl func()) *design.APIDefinition {
	if design.Design.Name != "" {
		dslengine.ReportError("multiple API definitions, only one is allowed")
		return nil
	}
	if !dslengine.IsTopLevelDefinition() {
		dslengine.IncompatibleDSL()
		return nil
	}

	if name == "" {
		dslengine.ReportError("API name cannot be empty")
	}
	design.Design.Name = name
	design.Design.DSLFunc = dsl
	return design.Design
}
Пример #4
0
// Type implements the type definition dsl. A type definition describes a data structure consisting
// of attributes. Each attribute has a type which can also refer to a type definition (or use a
// primitive type or nested attibutes). The dsl syntax for define a type definition is the
// Attribute dsl, see Attribute.
//
// On top of specifying any attribute type, type definitions can also be used to describe the data
// structure of a request payload. They can also be used by media type definitions as reference, see
// Reference. Here is an example:
//
//	Type("createPayload", func() {
//		Description("Type of create and upload action payloads")
//		Attribute("name", String, "name of bottle")
//		Attribute("origin", Origin, "Details on wine origin")  // See Origin definition below
//		Required("name")
//	})
//
//	var Origin = Type("origin", func() {
//		Description("Origin of bottle")
//		Attribute("Country")
//	})
//
// This function returns the newly defined type so the value can be used throughout the dsl.
func Type(name string, dsl func()) *design.UserTypeDefinition {
	if design.Design.Types == nil {
		design.Design.Types = make(map[string]*design.UserTypeDefinition)
	} else if _, ok := design.Design.Types[name]; ok {
		dslengine.ReportError("type %#v defined twice", name)
		return nil
	}

	if !dslengine.IsTopLevelDefinition() {
		dslengine.IncompatibleDSL()
		return nil
	}

	t := &design.UserTypeDefinition{
		TypeName:            name,
		AttributeDefinition: &design.AttributeDefinition{DSLFunc: dsl},
	}
	if dsl == nil {
		t.Type = design.String
	}
	design.Design.Types[name] = t
	return t
}
Пример #5
0
// MediaType implements the media type definition apidsl. A media type definition describes the
// representation of a resource used in a response body. This includes listing all the *potential*
// resource attributes that can appear in the body. Views specify which of the attributes are
// actually rendered so that the same media type definition may represent multiple rendering of a
// given resource representation.
//
// All media types must define a view named "default". This view is used to render the media type in
// response bodies when no other view is specified.
//
// A media type definition may also define links to other media types. This is done by first
// defining an attribute for the linked-to media type and then referring to that attribute in the
// Links apidsl. Views may then elect to render one or the other or both. Links are rendered using the
// special "link" view. Media types that are linked to must define that view. Here is an example
// showing all the possible media type sub-definitions:
//
//	MediaType("application/vnd.goa.example.bottle", func() {
//		Description("A bottle of wine")
//		TypeName("BottleMedia") 		// Optionally override the default generated name
//		Attributes(func() {
//			Attribute("id", Integer, "ID of bottle")
//			Attribute("href", String, "API href of bottle")
//			Attribute("account", Account, "Owner account")
//			Attribute("origin", Origin, "Details on wine origin")
//			Links(func() {
//				Link("account")		// Defines a link to the Account media type
//				Link("origin", "tiny")	// Overrides the default view used to render links
//			})
//			Required("id", "href")
//		})
//		View("default", func() {
//			Attribute("id")
//			Attribute("href")
//			Attribute("links")	// Default view renders links
//		})
//		View("extended", func() {
//			Attribute("id")
//			Attribute("href")
//			Attribute("account")	// Extended view renders account inline
//			Attribute("origin")	// Extended view renders origin inline
//			Attribute("links")	// Extended view also renders links
//		})
// 	})
//
// This function returns the media type definition so it can be referred to throughout the apidsl.
func MediaType(identifier string, apidsl func()) *design.MediaTypeDefinition {
	if design.Design.MediaTypes == nil {
		design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition)
	}

	if !dslengine.IsTopLevelDefinition() {
		dslengine.IncompatibleDSL()
		return nil
	}

	// Validate Media Type
	identifier, params, err := mime.ParseMediaType(identifier)
	if err != nil {
		dslengine.ReportError("invalid media type identifier %#v: %s",
			identifier, err)
		// We don't return so that other errors may be
		// captured in this one run.
		identifier = "text/plain"
	}
	canonicalID := design.CanonicalIdentifier(identifier)
	// Validate that media type identifier doesn't clash
	if _, ok := design.Design.MediaTypes[canonicalID]; ok {
		dslengine.ReportError("media type %#v is defined twice", identifier)
		return nil
	}
	parts := strings.Split(identifier, "+")
	// Make sure it has the `+json` suffix (TBD update when goa supports other encodings)
	if len(parts) > 1 {
		parts = parts[1:]
		found := false
		for _, part := range parts {
			if part == "json" {
				found = true
				break
			}
		}
		if !found {
			identifier += "+json"
		}
	}
	identifier = mime.FormatMediaType(identifier, params)
	// Concoct a Go type name from the identifier, should it be possible to set it in the apidsl?
	// pros: control the type name generated, cons: not needed in apidsl, adds one more thing to worry about
	lastPart := identifier
	lastPartIndex := strings.LastIndex(identifier, "/")
	if lastPartIndex > -1 {
		lastPart = identifier[lastPartIndex+1:]
	}
	plusIndex := strings.Index(lastPart, "+")
	if plusIndex > 0 {
		lastPart = lastPart[:plusIndex]
	}
	lastPart = strings.TrimPrefix(lastPart, "vnd.")
	elems := strings.Split(lastPart, ".")
	for i, e := range elems {
		elems[i] = strings.Title(e)
	}
	typeName := strings.Join(elems, "")
	if typeName == "" {
		mediaTypeCount++
		typeName = fmt.Sprintf("MediaType%d", mediaTypeCount)
	}
	// Now save the type in the API media types map
	mt := design.NewMediaTypeDefinition(typeName, identifier, apidsl)
	design.Design.MediaTypes[canonicalID] = mt
	return mt

}