Exemple #1
0
// CollectionOf creates a collection media type from its element media type. A collection media
// type represents the content of responses that return a collection of resources such as "list"
// actions. This function can be called from any place where a media type can be used.
// The resulting media type identifier is built from the element media type by appending the media
// type parameter "type" with value "collection".
func CollectionOf(v interface{}, dsl ...func()) *design.MediaTypeDefinition {
	if generatedMediaTypes == nil {
		generatedMediaTypes = make(map[string]*design.MediaTypeDefinition)
	}
	var m *design.MediaTypeDefinition
	var ok bool
	m, ok = v.(*design.MediaTypeDefinition)
	if !ok {
		if id, ok := v.(string); ok {
			m = design.Design.MediaTypes[design.CanonicalIdentifier(id)]
		}
	}
	if m == nil {
		ReportError("invalid CollectionOf argument: not a media type and not a known media type identifier")
		return nil
	}
	id := m.Identifier
	mediatype, params, err := mime.ParseMediaType(id)
	if err != nil {
		ReportError("invalid media type identifier %#v: %s", id, err)
		return nil
	}
	hasType := false
	for param := range params {
		if param == "type" {
			hasType = true
			break
		}
	}
	if !hasType {
		params["type"] = "collection"
	}
	id = mime.FormatMediaType(mediatype, params)
	typeName := m.TypeName + "Collection"
	if mt, ok := generatedMediaTypes[typeName]; ok {
		// Already have a type for this collection, reuse it.
		return mt
	}
	mt := design.NewMediaTypeDefinition(typeName, id, func() {
		if mt, ok := mediaTypeDefinition(true); ok {
			mt.TypeName = typeName
			mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)}
			if len(dsl) > 0 {
				executeDSL(dsl[0], mt)
			}
			if mt.Views == nil {
				// If the DSL didn't create any views (or there is no DSL at all)
				// then inherit the views from the collection element.
				mt.Views = make(map[string]*design.ViewDefinition)
				for n, v := range m.Views {
					mt.Views[n] = v
				}
			}
		}
	})
	// Do not execute the DSL right away, will be done last to make sure the element DSL has run
	// first.
	generatedMediaTypes[mt.TypeName] = mt
	return mt
}
Exemple #2
0
// CollectionOf creates a collection media type from its element media type. A collection media
// type represents the content of responses that return a collection of resources such as "list"
// actions. This function can be called from any place where a media type can be used.
// The resulting media type identifier is built from the element media type by appending the media
// type parameter "type" with value "collection".
func CollectionOf(m *design.MediaTypeDefinition, dsl ...func()) *design.MediaTypeDefinition {
	id := m.Identifier
	mediatype, params, err := mime.ParseMediaType(id)
	if err != nil {
		ReportError("invalid media type identifier %#v: %s", id, err)
		return nil
	}
	slash := strings.Index(mediatype, "/")
	if slash == -1 {
		mediatype += "/json"
	}
	hasType := false
	for param := range params {
		if param == "type" {
			hasType = true
			break
		}
	}
	if !hasType {
		params["type"] = "collection"
	}
	id = mime.FormatMediaType(mediatype, params)
	typeName := m.TypeName + "Collection"
	mt := design.NewMediaTypeDefinition(typeName, id, func() {
		if mt, ok := mediaTypeDefinition(true); ok {
			mt.TypeName = typeName
			mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)}
			if len(dsl) > 0 {
				executeDSL(dsl[0], mt)
			}
		}
	})
	if executeDSL(mt.DSL, mt) {
		design.Design.MediaTypes[id] = mt
	}
	return mt
}
Exemple #3
0
// MediaType implements the media type definition DSL. 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 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")
//		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 DSL.
func MediaType(identifier string, dsl func()) *design.MediaTypeDefinition {
	if design.Design == nil {
		InitDesign()
	}
	if design.Design.MediaTypes == nil {
		design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition)
	}
	if topLevelDefinition(true) {
		identifier, params, err := mime.ParseMediaType(identifier)
		if err != nil {
			ReportError("invalid media type identifier %#v: %s",
				identifier, err)
		}
		slash := strings.Index(identifier, "/")
		if slash == -1 {
			identifier += "/json"
		}
		identifier = mime.FormatMediaType(identifier, params)
		elems := strings.Split(identifier, ".")
		elems = strings.Split(elems[len(elems)-1], "/")
		elems = strings.Split(elems[0], "+")
		typeName := inflect.Camelize(elems[0])
		if typeName == "" {
			mediaTypeCount++
			typeName = fmt.Sprintf("MediaType%d", mediaTypeCount)
		}
		if _, ok := design.Design.MediaTypes[identifier]; ok {
			ReportError("media type %#v is defined twice", identifier)
			return nil
		}
		mt := design.NewMediaTypeDefinition(typeName, identifier, dsl)
		design.Design.MediaTypes[identifier] = mt
		return mt
	}
	return nil
}
Exemple #4
0
// MediaType implements the media type definition DSL. 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 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")
//		APIVersion("1.0")
//		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 DSL.
func MediaType(identifier string, dsl func()) *design.MediaTypeDefinition {
	if design.Design == nil {
		InitDesign()
	}
	if design.Design.MediaTypes == nil {
		design.Design.MediaTypes = make(map[string]*design.MediaTypeDefinition)
	}
	if topLevelDefinition(true) {
		// Validate Media Type
		identifier, params, err := mime.ParseMediaType(identifier)
		if err != nil {
			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 = "plain/text"
		}
		canonicalID := design.CanonicalIdentifier(identifier)
		// Validate that media type identifier doesn't clash
		if _, ok := design.Design.MediaTypes[canonicalID]; ok {
			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 DSL?
		// pros: control the type name generated, cons: not needed in DSL, 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, dsl)
		design.Design.MediaTypes[canonicalID] = mt
		return mt
	}
	return nil
}