Пример #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
}
Пример #2
0
// AddMultipart creates a nested part with mediaType and a randomly generated
// boundary. The returned nested part can then be used to add a text or
// an attachment.
//
// Example:
// 	alt, _ := part.AddMultipart("multipart/mixed")
// 	alt.AddText("text/plain", text)
// 	alt.AddAttachment("gopher.png", "", image)
// 	alt.Close()
func (p *Multipart) AddMultipart(mediaType string) (nested *Multipart, err error) {
	if !strings.HasPrefix(mediaType, "multipart") {
		return nil, errors.New("mail: mediaType must start with the word \"multipart\" as in multipart/mixed or multipart/alter")
	}

	if p.isClosed {
		return nil, ErrPartClosed
	}

	boundary := randomString(boundaryLength)

	// Mutlipart management
	var mimeType string
	if strings.HasPrefix(mediaType, "multipart") {
		mimeType = mime.FormatMediaType(
			mediaType,
			map[string]string{"boundary": boundary},
		)
	} else {
		mimeType = mediaType
	}

	// Header
	p.header = make(textproto.MIMEHeader)
	p.header["Content-Type"] = []string{mimeType}

	w, err := p.writer.CreatePart(p.header)
	if err != nil {
		return nil, err
	}

	nested = createPart(w, p.header, mediaType, boundary)
	return nested, nil
}
Пример #3
0
func (post *Post) contentType() string {
	params := map[string]string{"type": post.Type}
	if post.Notification {
		params["rel"] = "https://tent.io/rels/notification"
	}
	return mime.FormatMediaType(MediaTypePost, params)
}
Пример #4
0
func fmtContentType(s string) string {
	m, p, err := mime.ParseMediaType(s)
	if err != nil {
		return s
	}
	return mime.FormatMediaType(m, p)
}
Пример #5
0
func Fuzz(data []byte) int {
	sdata := string(data)
	mt, params, err := mime.ParseMediaType(sdata)
	if err != nil {
		return 0
	}
	sdata1 := mime.FormatMediaType(mt, params)
	mt1, params1, err := mime.ParseMediaType(sdata1)
	if err != nil {
		if err.Error() == "mime: no media type" {
			// https://github.com/golang/go/issues/11289
			return 0
		}
		if err.Error() == "mime: invalid media parameter" {
			// https://github.com/golang/go/issues/11290
			return 0
		}
		fmt.Printf("%q(%q, %+v) -> %q\n", sdata, mt, params, sdata1)
		panic(err)
	}
	if !fuzz.DeepEqual(mt, mt1) {
		fmt.Printf("%q -> %q\n", mt, mt1)
		panic("mediatype changed")
	}
	if !fuzz.DeepEqual(params, params1) {
		fmt.Printf("%+v -> %+v\n", params, params1)
		panic("params changed")
	}
	return 1
}
Пример #6
0
// IsError returns true if the media type is implemented via a goa struct.
func (m *MediaTypeDefinition) IsError() bool {
	base, params, err := mime.ParseMediaType(m.Identifier)
	if err != nil {
		panic("invalid media type identifier " + m.Identifier) // bug
	}
	delete(params, "view")
	return mime.FormatMediaType(base, params) == ErrorMedia.Identifier
}
Пример #7
0
// projectIdentifier computes the projected media type identifier by adding the "view" param.  We
// need the projected media type identifier to be different so that looking up projected media types
// from ProjectedMediaTypes works correctly. It's also good for clients.
func (m *MediaTypeDefinition) projectIdentifier(view string) string {
	base, params, err := mime.ParseMediaType(m.Identifier)
	if err != nil {
		base = m.Identifier
	}
	params["view"] = view
	return mime.FormatMediaType(base, params)
}
Пример #8
0
// SetContentType returns the MIME type of the message.
func (m *Message) SetContentType(mediaType string) {
	_, params, _ := mime.ParseMediaType(m.GetHeader("Content-Type"))
	if m.root != nil {
		params["boundary"] = m.root.Boundary()
	}

	m.SetHeader("Content-Type", mime.FormatMediaType(mediaType, params))
}
Пример #9
0
// projectIdentifier computes the projected canonical media type identifier by adding the "view"
// param if the view is not the default view.
func (m *MediaTypeDefinition) projectCanonical(view string) string {
	cano := CanonicalIdentifier(m.Identifier)
	base, params, _ := mime.ParseMediaType(cano)
	if params["view"] != "" {
		return cano // Already projected
	}
	params["view"] = view
	return mime.FormatMediaType(base, params)
}
Пример #10
0
func MediaType(name string, params ...string) string {
	if len(params)%2 != 0 {
		panic(errors.New("params must be an even sized list"))
	}
	p := make(map[string]string, len(params)/2)
	for i := 0; i < len(params); i += 2 {
		p[params[i]] = params[i+1]
	}
	return mime.FormatMediaType(name, p)
}
Пример #11
0
// NewMultipart modifies msg to become a multipart message and returns the root
// part inside which other parts, texts and attachments can be nested.
//
// Example:
// 	multipart := NewMultipart("multipart/alternative", msg)
// 	multipart.AddPart("text/plain", text)
// 	multipart.AddPart("text/html", html)
// 	multipart.Close()
func NewMultipart(mediaType string, msg *Message) (root *Multipart) {
	boundary := randomString(boundaryLength)
	msg.root = createPart(msg.Body, make(textproto.MIMEHeader), mediaType, boundary)
	_, params, _ := mime.ParseMediaType(msg.GetHeader("Content-Type"))
	if params == nil {
		params = make(map[string]string)
	}
	params["boundary"] = boundary
	msg.SetHeader("Content-Type", mime.FormatMediaType(mediaType, params))
	return msg.root
}
Пример #12
0
// CanonicalIdentifier returns the media type identifier sans suffix
// which is what the DSL uses to store and lookup media types.
func CanonicalIdentifier(identifier string) string {
	base, params, err := mime.ParseMediaType(identifier)
	if err != nil {
		return identifier
	}
	id := base
	if i := strings.Index(id, "+"); i != -1 {
		id = id[:i]
	}
	return mime.FormatMediaType(id, params)
}
Пример #13
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
}
Пример #14
0
// SetBodyWithCharset translates and sets the body according to given charset.
//
// Header field Content-Transfer-Encoding is set to DefaultTransferEncoding.
// Header field Content-Type is set according to charset.
// All lines are modified to ensure CRLF.
//
// Use SetBody to use default character encoding.
func (m *Message) SetBodyWithCharset(charset, body string) error {
	m.Header.Set(HEADER_CONTENT_TRANSFER_ENCODING, DefaultTransferEncoding)
	m.Header.Set(HEADER_CONTENT_TYPE, mime.FormatMediaType(
		"text/plain",
		map[string]string{"charset": DefaultCharset},
	))

	bytes, err := StringToBody(body, DefaultCharset)
	if err != nil {
		return err
	}

	m.body = bytes
	m.Header.Set(HEADER_BODY, fmt.Sprintf("%d", len(bytes)))
	return nil
}
Пример #15
0
func (ar assertResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	t := asserts.MediaType
	if ar.bundle {
		t = mime.FormatMediaType(t, map[string]string{"bundle": "y"})
	}
	w.Header().Set("Content-Type", t)
	w.Header().Set("X-Ubuntu-Assertions-Count", strconv.Itoa(len(ar.assertions)))
	enc := asserts.NewEncoder(w)
	for _, a := range ar.assertions {
		err := enc.Encode(a)
		if err != nil {
			logger.Noticef("unable to write encoded assertion into response: %v", err)
			break

		}
	}
}
Пример #16
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
}
Пример #17
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
}
Пример #18
0
// formatMediaType serializes mediatype t and the parameters
// param as a media type conforming to RFC 2045 and RFC 2616.
// The type and parameter names are written in lower-case.
// When any of the arguments result in a standard violation then
// formatMediaType returns the empty string.
func formatMediaType(t string, param map[string]string) string {
	return mime.FormatMediaType(t, param)
}
Пример #19
0
// ContentType returns an RFC 2045 compatible internet media type string.
func (res *Response) ContentType() string {
	return mime.FormatMediaType(res.MediaType, map[string]string{"charset": res.Charset})
}
Пример #20
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{}, apidsl ...func()) *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 {
		dslengine.ReportError("invalid CollectionOf argument: not a media type and not a known media type identifier")
		// don't return nil to avoid panics, the error will get reported at the end
		return design.NewMediaTypeDefinition("InvalidCollection", "text/plain", nil)
	}
	id := m.Identifier
	mediatype, params, err := mime.ParseMediaType(id)
	if err != nil {
		dslengine.ReportError("invalid media type identifier %#v: %s", id, err)
		// don't return nil to avoid panics, the error will get reported at the end
		return design.NewMediaTypeDefinition("InvalidCollection", "text/plain", nil)
	}
	hasType := false
	for param := range params {
		if param == "type" {
			hasType = true
			break
		}
	}
	if !hasType {
		params["type"] = "collection"
	}
	id = mime.FormatMediaType(mediatype, params)
	canonical := design.CanonicalIdentifier(id)
	if mt, ok := design.GeneratedMediaTypes[canonical]; ok {
		// Already have a type for this collection, reuse it.
		return mt
	}
	mt := design.NewMediaTypeDefinition("", id, func() {
		if mt, ok := mediaTypeDefinition(); ok {
			// Cannot compute collection type name before element media type DSL has executed
			// since the DSL may modify element type name via the TypeName function.
			mt.TypeName = m.TypeName + "Collection"
			mt.AttributeDefinition = &design.AttributeDefinition{Type: ArrayOf(m)}
			if len(apidsl) > 0 {
				dslengine.Execute(apidsl[0], mt)
			}
			if mt.Views == nil {
				// If the apidsl didn't create any views (or there is no apidsl 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 apidsl right away, will be done last to make sure the element apidsl has run
	// first.
	design.GeneratedMediaTypes[canonical] = mt
	return mt
}
Пример #21
0
// ServeHTTP streams a zip archive of all the files "under"
// zh.root. That is, all the files pointed by file permanodes,
// which are directly members of zh.root or recursively down
// directory permanodes and permanodes members.
// To build the fullpath of a file in a collection, it uses
// the collection title if present, its blobRef otherwise, as
// a directory name.
func (zh *zipHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	// TODO: use http.ServeContent, so Range requests work and downloads can be resumed.
	// Will require calculating the zip length once first (ideally as cheaply as possible,
	// with dummy counting writer and dummy all-zero-byte-files of a fixed size),
	// and then making a dummy ReadSeeker for ServeContent that can seek to the end,
	// and then seek back to the beginning, but then seeks forward make it remember
	// to skip that many bytes from the archive/zip writer when answering Reads.
	if !httputil.IsGet(req) {
		http.Error(rw, "Invalid method", http.StatusMethodNotAllowed)
		return
	}
	bf, err := zh.blobList("/", zh.root)
	if err != nil {
		log.Printf("Could not serve zip for %v: %v", zh.root, err)
		http.Error(rw, "Server error", http.StatusInternalServerError)
		return
	}
	blobFiles := renameDuplicates(bf)

	// TODO(mpl): streaming directly won't work on appengine if the size goes
	// over 32 MB. Deal with that.
	h := rw.Header()
	h.Set("Content-Type", "application/zip")
	filename := zh.filename
	if filename == "" {
		filename = "download.zip"
	}
	h.Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename": filename}))
	zw := zip.NewWriter(rw)
	etag := sha1.New()
	for _, file := range blobFiles {
		etag.Write([]byte(file.blobRef.String()))
	}
	h.Set("Etag", fmt.Sprintf(`"%x"`, etag.Sum(nil)))

	for _, file := range blobFiles {
		fr, err := schema.NewFileReader(zh.storageSeekFetcher(), file.blobRef)
		if err != nil {
			log.Printf("Can not add %v in zip, not a file: %v", file.blobRef, err)
			http.Error(rw, "Server error", http.StatusInternalServerError)
			return
		}
		f, err := zw.CreateHeader(
			&zip.FileHeader{
				Name:   file.path,
				Method: zip.Store,
			})
		if err != nil {
			log.Printf("Could not create %q in zip: %v", file.path, err)
			http.Error(rw, "Server error", http.StatusInternalServerError)
			return
		}
		_, err = io.Copy(f, fr)
		fr.Close()
		if err != nil {
			log.Printf("Could not zip %q: %v", file.path, err)
			return
		}
	}
	err = zw.Close()
	if err != nil {
		log.Printf("Could not close zipwriter: %v", err)
		return
	}
}
Пример #22
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
}
Пример #23
0
func (pm *ParsedMimeType) String() string {
	return mime.FormatMediaType(pm.MediaType, pm.Params)
}