github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/design/api.go (about)

     1  package design
     2  
     3  import (
     4  	"mime"
     5  	"regexp"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/goadesign/goa/dslengine"
    10  )
    11  
    12  // MediaTypeRoot is the data structure that represents the additional DSL definition root
    13  // that contains the media type definition set created by CollectionOf index by canonical id.
    14  type MediaTypeRoot map[string]*MediaTypeDefinition
    15  
    16  // List of all built-in response names.
    17  const (
    18  	Continue           = "Continue"
    19  	SwitchingProtocols = "SwitchingProtocols"
    20  
    21  	OK                   = "OK"
    22  	Created              = "Created"
    23  	Accepted             = "Accepted"
    24  	NonAuthoritativeInfo = "NonAuthoritativeInfo"
    25  	NoContent            = "NoContent"
    26  	ResetContent         = "ResetContent"
    27  	PartialContent       = "PartialContent"
    28  
    29  	MultipleChoices   = "MultipleChoices"
    30  	MovedPermanently  = "MovedPermanently"
    31  	Found             = "Found"
    32  	SeeOther          = "SeeOther"
    33  	NotModified       = "NotModified"
    34  	UseProxy          = "UseProxy"
    35  	TemporaryRedirect = "TemporaryRedirect"
    36  
    37  	BadRequest                   = "BadRequest"
    38  	Unauthorized                 = "Unauthorized"
    39  	PaymentRequired              = "PaymentRequired"
    40  	Forbidden                    = "Forbidden"
    41  	NotFound                     = "NotFound"
    42  	MethodNotAllowed             = "MethodNotAllowed"
    43  	NotAcceptable                = "NotAcceptable"
    44  	ProxyAuthRequired            = "ProxyAuthRequired"
    45  	RequestTimeout               = "RequestTimeout"
    46  	Conflict                     = "Conflict"
    47  	Gone                         = "Gone"
    48  	LengthRequired               = "LengthRequired"
    49  	PreconditionFailed           = "PreconditionFailed"
    50  	RequestEntityTooLarge        = "RequestEntityTooLarge"
    51  	RequestURITooLong            = "RequestURITooLong"
    52  	UnsupportedMediaType         = "UnsupportedMediaType"
    53  	RequestedRangeNotSatisfiable = "RequestedRangeNotSatisfiable"
    54  	ExpectationFailed            = "ExpectationFailed"
    55  	Teapot                       = "Teapot"
    56  	UnprocessableEntity          = "UnprocessableEntity"
    57  
    58  	InternalServerError     = "InternalServerError"
    59  	NotImplemented          = "NotImplemented"
    60  	BadGateway              = "BadGateway"
    61  	ServiceUnavailable      = "ServiceUnavailable"
    62  	GatewayTimeout          = "GatewayTimeout"
    63  	HTTPVersionNotSupported = "HTTPVersionNotSupported"
    64  )
    65  
    66  var (
    67  	// Design being built by DSL.
    68  	Design *APIDefinition
    69  
    70  	// GeneratedMediaTypes contains DSL definitions that were created by the design DSL and
    71  	// need to be executed as a second pass.
    72  	// An example of this are media types defined with CollectionOf: the element media type
    73  	// must be defined first then the definition created by CollectionOf must execute.
    74  	GeneratedMediaTypes MediaTypeRoot
    75  
    76  	// ProjectedMediaTypes is a cache used by the MediaType strut Project method.
    77  	ProjectedMediaTypes MediaTypeRoot
    78  
    79  	// WildcardRegex is the regular expression used to capture path parameters.
    80  	WildcardRegex = regexp.MustCompile(`/(?::|\*)([a-zA-Z0-9_]+)`)
    81  
    82  	// DefaultDecoders contains the decoding definitions used when no Consumes DSL is found.
    83  	DefaultDecoders []*EncodingDefinition
    84  
    85  	// DefaultEncoders contains the encoding definitions used when no Produces DSL is found.
    86  	DefaultEncoders []*EncodingDefinition
    87  
    88  	// KnownEncoders contains the list of encoding packages and factories known by goa indexed
    89  	// by MIME type.
    90  	KnownEncoders = map[string]string{
    91  		"application/json":      "github.com/goadesign/goa",
    92  		"application/xml":       "github.com/goadesign/goa",
    93  		"application/gob":       "github.com/goadesign/goa",
    94  		"application/x-gob":     "github.com/goadesign/goa",
    95  		"application/binc":      "github.com/goadesign/goa/encoding/binc",
    96  		"application/x-binc":    "github.com/goadesign/goa/encoding/binc",
    97  		"application/cbor":      "github.com/goadesign/goa/encoding/cbor",
    98  		"application/x-cbor":    "github.com/goadesign/goa/encoding/cbor",
    99  		"application/msgpack":   "github.com/goadesign/goa/encoding/msgpack",
   100  		"application/x-msgpack": "github.com/goadesign/goa/encoding/msgpack",
   101  	}
   102  
   103  	// KnownEncoderFunctions contains the list of encoding encoder and decoder functions known
   104  	// by goa indexed by MIME type.
   105  	KnownEncoderFunctions = map[string][2]string{
   106  		"application/json":      {"NewJSONEncoder", "NewJSONDecoder"},
   107  		"application/xml":       {"NewXMLEncoder", "NewXMLDecoder"},
   108  		"application/gob":       {"NewGobEncoder", "NewGobDecoder"},
   109  		"application/x-gob":     {"NewGobEncoder", "NewGobDecoder"},
   110  		"application/binc":      {"NewEncoder", "NewDecoder"},
   111  		"application/x-binc":    {"NewEncoder", "NewDecoder"},
   112  		"application/cbor":      {"NewEncoder", "NewDecoder"},
   113  		"application/x-cbor":    {"NewEncoder", "NewDecoder"},
   114  		"application/msgpack":   {"NewEncoder", "NewDecoder"},
   115  		"application/x-msgpack": {"NewEncoder", "NewDecoder"},
   116  	}
   117  
   118  	// JSONContentTypes list the Content-Type header values that cause goa to encode or decode
   119  	// JSON by default.
   120  	JSONContentTypes = []string{"application/json"}
   121  
   122  	// XMLContentTypes list the Content-Type header values that cause goa to encode or decode
   123  	// XML by default.
   124  	XMLContentTypes = []string{"application/xml"}
   125  
   126  	// GobContentTypes list the Content-Type header values that cause goa to encode or decode
   127  	// Gob by default.
   128  	GobContentTypes = []string{"application/gob", "application/x-gob"}
   129  
   130  	// ErrorMediaIdentifier is the media type identifier used for error responses.
   131  	ErrorMediaIdentifier = "application/vnd.goa.error"
   132  
   133  	// ErrorMedia is the built-in media type for error responses.
   134  	ErrorMedia = &MediaTypeDefinition{
   135  		UserTypeDefinition: &UserTypeDefinition{
   136  			AttributeDefinition: &AttributeDefinition{
   137  				Type:        errorMediaType,
   138  				Description: "Error response media type",
   139  				Example: map[string]interface{}{
   140  					"id":     "3F1FKVRR",
   141  					"status": "400",
   142  					"code":   "invalid_value",
   143  					"detail": "Value of ID must be an integer",
   144  					"meta":   map[string]interface{}{"timestamp": 1458609066},
   145  				},
   146  			},
   147  			TypeName: "error",
   148  		},
   149  		Identifier: ErrorMediaIdentifier,
   150  		Views:      map[string]*ViewDefinition{"default": errorMediaView},
   151  	}
   152  
   153  	errorMediaType = Object{
   154  		"id": &AttributeDefinition{
   155  			Type:        String,
   156  			Description: "a unique identifier for this particular occurrence of the problem.",
   157  			Example:     "3F1FKVRR",
   158  		},
   159  		"status": &AttributeDefinition{
   160  			Type:        String,
   161  			Description: "the HTTP status code applicable to this problem, expressed as a string value.",
   162  			Example:     "400",
   163  		},
   164  		"code": &AttributeDefinition{
   165  			Type:        String,
   166  			Description: "an application-specific error code, expressed as a string value.",
   167  			Example:     "invalid_value",
   168  		},
   169  		"detail": &AttributeDefinition{
   170  			Type:        String,
   171  			Description: "a human-readable explanation specific to this occurrence of the problem.",
   172  			Example:     "Value of ID must be an integer",
   173  		},
   174  		"meta": &AttributeDefinition{
   175  			Type: &Hash{
   176  				KeyType:  &AttributeDefinition{Type: String},
   177  				ElemType: &AttributeDefinition{Type: Any},
   178  			},
   179  			Description: "a meta object containing non-standard meta-information about the error.",
   180  			Example:     map[string]interface{}{"timestamp": 1458609066},
   181  		},
   182  	}
   183  
   184  	errorMediaView = &ViewDefinition{
   185  		AttributeDefinition: &AttributeDefinition{Type: errorMediaType},
   186  		Name:                "default",
   187  	}
   188  )
   189  
   190  func init() {
   191  	goa := "github.com/goadesign/goa"
   192  	DefaultEncoders = []*EncodingDefinition{
   193  		{MIMETypes: JSONContentTypes, PackagePath: goa, Function: "NewJSONEncoder"},
   194  		{MIMETypes: XMLContentTypes, PackagePath: goa, Function: "NewXMLEncoder"},
   195  		{MIMETypes: GobContentTypes, PackagePath: goa, Function: "NewGobEncoder"},
   196  	}
   197  	DefaultDecoders = []*EncodingDefinition{
   198  		{MIMETypes: JSONContentTypes, PackagePath: goa, Function: "NewJSONDecoder"},
   199  		{MIMETypes: XMLContentTypes, PackagePath: goa, Function: "NewXMLDecoder"},
   200  		{MIMETypes: GobContentTypes, PackagePath: goa, Function: "NewGobDecoder"},
   201  	}
   202  	errorMediaView.Parent = ErrorMedia
   203  }
   204  
   205  // CanonicalIdentifier returns the media type identifier sans suffix
   206  // which is what the DSL uses to store and lookup media types.
   207  func CanonicalIdentifier(identifier string) string {
   208  	base, params, err := mime.ParseMediaType(identifier)
   209  	if err != nil {
   210  		return identifier
   211  	}
   212  	id := base
   213  	if i := strings.Index(id, "+"); i != -1 {
   214  		id = id[:i]
   215  	}
   216  	return mime.FormatMediaType(id, params)
   217  }
   218  
   219  // HasKnownEncoder returns true if the encoder for the given MIME type is known by goa.
   220  // MIME types with unknown encoders must be associated with a package path explicitly in the DSL.
   221  func HasKnownEncoder(mimeType string) bool {
   222  	return KnownEncoders[mimeType] != ""
   223  }
   224  
   225  // ExtractWildcards returns the names of the wildcards that appear in path.
   226  func ExtractWildcards(path string) []string {
   227  	matches := WildcardRegex.FindAllStringSubmatch(path, -1)
   228  	wcs := make([]string, len(matches))
   229  	for i, m := range matches {
   230  		wcs[i] = m[1]
   231  	}
   232  	return wcs
   233  }
   234  
   235  // DSLName is displayed to the user when the DSL executes.
   236  func (r MediaTypeRoot) DSLName() string {
   237  	return "Generated Media Types"
   238  }
   239  
   240  // DependsOn return the DSL roots the generated media types DSL root depends on, that's the API DSL.
   241  func (r MediaTypeRoot) DependsOn() []dslengine.Root {
   242  	return []dslengine.Root{Design}
   243  }
   244  
   245  // IterateSets iterates over the one generated media type definition set.
   246  func (r MediaTypeRoot) IterateSets(iterator dslengine.SetIterator) {
   247  	canonicalIDs := make([]string, len(r))
   248  	i := 0
   249  	for _, mt := range r {
   250  		canonicalID := CanonicalIdentifier(mt.Identifier)
   251  		Design.MediaTypes[canonicalID] = mt
   252  		canonicalIDs[i] = canonicalID
   253  		i++
   254  	}
   255  	sort.Strings(canonicalIDs)
   256  	set := make([]dslengine.Definition, len(canonicalIDs))
   257  	for i, cid := range canonicalIDs {
   258  		set[i] = Design.MediaTypes[cid]
   259  	}
   260  	iterator(set)
   261  }
   262  
   263  // Reset deletes all the keys.
   264  func (r MediaTypeRoot) Reset() {
   265  	for k := range r {
   266  		delete(r, k)
   267  	}
   268  }