├── LICENSE ├── README.md ├── TODO.md ├── cmd ├── root.go ├── swagger.go ├── template.go └── template_test.go └── main.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Taichi Sasaki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ago 2 | 3 | A tool to generate a design definition of [goa](https://github.com/goadesign/goa) from a swagger definition. 4 | 5 | [![license](https://img.shields.io/github/license/tchssk/ago.svg)]() 6 | 7 | 8 | ## Installation 9 | 10 | ```sh 11 | $ go get github.com/tchssk/ago 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```sh 17 | $ ago swagger swagger.json > design.go 18 | ``` 19 | 20 | ## Notes 21 | 22 | This application is currently under construction. You may have to modify the generated swagger definition before using from [goagen](https://github.com/goadesign/goa/goagen). Features to be implemented are described in [TODO.md](TODO.md). Contributions are welcomed! 23 | 24 | ## License 25 | 26 | MIT License 27 | 28 | ## Author 29 | 30 | Taichi Sasaki 31 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## Swagger 2 | 3 | `swaggerToAPI()` in [cmd/swagger.go](cmd/swagger.go) converts `swagger.Swagger` to `design.APIDefinition`. 4 | 5 | - [x] Name 6 | - [x] Title 7 | - [x] Description 8 | - [x] Version 9 | - [x] Host 10 | - [x] Schemes 11 | - [x] BasePath 12 | - [ ] Params 13 | - [x] Consumes 14 | - [x] Produces 15 | - [x] Origins 16 | - [x] TermsOfService 17 | - [x] Contact 18 | - [x] License 19 | - [x] Docs 20 | - [ ] Resources 21 | - [ ] Types 22 | - [ ] MediaTypes 23 | - [ ] Traits 24 | - [ ] Responses 25 | - [ ] ResponseTemplates 26 | - [ ] DefaultResponses 27 | - [ ] DefaultResponseTemplates 28 | - [ ] DSLFunc 29 | - [ ] Metadata 30 | - [ ] SecuritySchemes 31 | - [ ] Security 32 | - [ ] NoExamples 33 | 34 | ## Templates for DSL 35 | 36 | All goa DSL functions are generated by templates in [cmd/template.go](cmd/template.go). Templates are grouped into the following three types. 37 | 38 | - Components that have single value 39 | - Components that have multiple values 40 | - Containers 41 | 42 | ### DSL functions 43 | 44 | - [ ] func API(name string, dsl func()) *design.APIDefinition 45 | - [ ] func APIKeySecurity(name string, dsl ...func()) *design.SecuritySchemeDefinition 46 | - [ ] func AccessCodeFlow(authorizationURL, tokenURL string) 47 | - [ ] func Action(name string, dsl func()) 48 | - [ ] func ApplicationFlow(tokenURL string) 49 | - [ ] func ArrayOf(v interface{}, dsl ...func()) *design.Array 50 | - [ ] func Attribute(name string, args ...interface{}) 51 | - [ ] func Attributes(apidsl func()) 52 | - [x] func BasePath(val string) 53 | - [ ] func BasicAuthSecurity(name string, dsl ...func()) *design.SecuritySchemeDefinition 54 | - [x] func CONNECT(path string, dsl ...func()) *design.RouteDefinition 55 | - [x] func CanonicalActionName(a string) 56 | - [ ] func CollectionOf(v interface{}, apidsl ...func()) *design.MediaTypeDefinition 57 | - [x] func Consumes(args ...interface{}) 58 | - [x] func Contact(dsl func()) 59 | - [x] func ContentType(typ string) 60 | - [x] func Credentials() 61 | - [x] func DELETE(path string, dsl ...func()) *design.RouteDefinition 62 | - [ ] func Default(def interface{}) 63 | - [ ] func DefaultMedia(val interface{}, viewName ...string) 64 | - [x] func Description(d string) 65 | - [x] func Docs(dsl func()) 66 | - [x] func Email(email string) 67 | - [ ] func Enum(val ...interface{}) 68 | - [ ] func Example(exp interface{}) 69 | - [x] func Expose(vals ...string) 70 | - [ ] func Files(path, filename string, dsls ...func()) 71 | - [ ] func Format(f string) 72 | - [x] func Function(fn string) 73 | - [x] func GET(path string, dsl ...func()) *design.RouteDefinition 74 | - [x] func HEAD(path string, dsl ...func()) *design.RouteDefinition 75 | - [ ] func HashOf(k, v design.DataType) *design.Hash 76 | - [ ] func Header(name string, args ...interface{}) 77 | - [ ] func Headers(params ...interface{}) 78 | - [x] func Host(host string) 79 | - [ ] func ImplicitFlow(authorizationURL string) 80 | - [ ] func JWTSecurity(name string, dsl ...func()) *design.SecuritySchemeDefinition 81 | - [x] func License(dsl func()) 82 | - [ ] func Link(name string, view ...string) 83 | - [ ] func Links(apidsl func()) 84 | - [x] func MaxAge(val uint) 85 | - [x] func MaxLength(val int) 86 | - [ ] func Maximum(val interface{}) 87 | - [ ] func Media(val interface{}, viewName ...string) 88 | - [ ] func MediaType(identifier string, apidsl func()) *design.MediaTypeDefinition 89 | - [ ] func Member(name string, args ...interface{}) 90 | - [ ] func Metadata(name string, value ...string) 91 | - [x] func Methods(vals ...string) 92 | - [x] func MinLength(val int) 93 | - [ ] func Minimum(val interface{}) 94 | - [x] func Name(name string) 95 | - [ ] func NoExample() 96 | - [ ] func NoSecurity() 97 | - [ ] func OAuth2Security(name string, dsl ...func()) *design.SecuritySchemeDefinition 98 | - [x] func OPTIONS(path string, dsl ...func()) *design.RouteDefinition 99 | - [ ] func OptionalPayload(p interface{}, dsls ...func()) 100 | - [x] func Origin(origin string, dsl func()) 101 | - [x] func PATCH(path string, dsl ...func()) *design.RouteDefinition 102 | - [x] func POST(path string, dsl ...func()) *design.RouteDefinition 103 | - [x] func PUT(path string, dsl ...func()) *design.RouteDefinition 104 | - [x] func Package(path string) 105 | - [ ] func Param(name string, args ...interface{}) 106 | - [ ] func Params(dsl func()) 107 | - [ ] func Parent(p string) 108 | - [ ] func PasswordFlow(tokenURL string) 109 | - [ ] func Pattern(p string) 110 | - [x] func Payload(p interface{}, dsls ...func()) 111 | - [x] func Produces(args ...interface{}) 112 | - [ ] func Query(parameterName string) 113 | - [ ] func Reference(t design.DataType) 114 | - [ ] func Required(names ...string) 115 | - [ ] func Resource(name string, dsl func()) *design.ResourceDefinition 116 | - [ ] func Response(name string, paramsAndDSL ...interface{}) 117 | - [ ] func ResponseTemplate(name string, p interface{}) 118 | - [x] func Routing(routes ...*design.RouteDefinition) 119 | - [x] func Scheme(vals ...string) 120 | - [ ] func Scope(name string, desc ...string) 121 | - [ ] func Security(scheme interface{}, dsl ...func()) 122 | - [x] func Status(status int) 123 | - [x] func TRACE(path string, dsl ...func()) *design.RouteDefinition 124 | - [x] func TermsOfService(terms string) 125 | - [x] func Title(val string) 126 | - [ ] func TokenURL(tokenURL string) 127 | - [ ] func Trait(name string, val ...func()) 128 | - [ ] func Type(name string, dsl func()) *design.UserTypeDefinition 129 | - [x] func TypeName(name string) 130 | - [x] func URL(url string) 131 | - [ ] func UseTrait(names ...string) 132 | - [x] func Version(ver string) 133 | - [ ] func View(name string, apidsl ...func()) 134 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | ) 10 | 11 | var cfgFile string 12 | 13 | // RootCmd represents the base command when called without any subcommands 14 | var RootCmd = &cobra.Command{ 15 | Use: "ago", 16 | Short: "A tool to generate design definitions of goa from swagger definitions.", 17 | Long: `A tool to generate design definitions of goa from swagger definitions.`, 18 | // Uncomment the following line if your bare application 19 | // has an action associated with it: 20 | // Run: func(cmd *cobra.Command, args []string) { }, 21 | } 22 | 23 | // Execute adds all child commands to the root command sets flags appropriately. 24 | // This is called by main.main(). It only needs to happen once to the rootCmd. 25 | func Execute() { 26 | if err := RootCmd.Execute(); err != nil { 27 | fmt.Println(err) 28 | os.Exit(-1) 29 | } 30 | } 31 | 32 | func init() { 33 | cobra.OnInitialize(initConfig) 34 | 35 | // Here you will define your flags and configuration settings. 36 | // Cobra supports Persistent Flags, which, if defined here, 37 | // will be global for your application. 38 | 39 | RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ago.yaml)") 40 | // Cobra also supports local flags, which will only run 41 | // when this action is called directly. 42 | RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 43 | } 44 | 45 | // initConfig reads in config file and ENV variables if set. 46 | func initConfig() { 47 | if cfgFile != "" { // enable ability to specify config file via flag 48 | viper.SetConfigFile(cfgFile) 49 | } 50 | 51 | viper.SetConfigName(".ago") // name of config file (without extension) 52 | viper.AddConfigPath("$HOME") // adding home directory as first search path 53 | viper.AutomaticEnv() // read in environment variables that match 54 | 55 | // If a config file is found, read it in. 56 | if err := viper.ReadInConfig(); err == nil { 57 | fmt.Println("Using config file:", viper.ConfigFileUsed()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/swagger.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "go/format" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | 12 | "github.com/goadesign/goa/design" 13 | "github.com/goadesign/goa/goagen/gen_swagger" 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | // swaggerCmd represents the swagger command 18 | var swaggerCmd = &cobra.Command{ 19 | Use: "swagger", 20 | Short: "Generate design from swagger definitions", 21 | Long: `Generate design from swagger definitions`, 22 | Run: func(cmd *cobra.Command, args []string) { 23 | if len(args) != 1 { 24 | log.Fatal("invalid file path") 25 | return 26 | } 27 | data, err := ioutil.ReadFile(args[0]) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | var swagger genswagger.Swagger 32 | if err := json.Unmarshal(data, &swagger); err != nil { 33 | log.Fatal(err) 34 | } 35 | buf := new(bytes.Buffer) 36 | if err := tmpl.ExecuteTemplate(buf, "all", swaggerToAPI(swagger)); err != nil { 37 | log.Fatal(err) 38 | } 39 | formated, err := format.Source(buf.Bytes()) 40 | if err != nil { 41 | fmt.Println(string(buf.Bytes())) 42 | log.Fatal(err) 43 | } 44 | if _, err := bytes.NewBuffer(formated).WriteTo(os.Stdout); err != nil { 45 | log.Fatal(err) 46 | } 47 | }, 48 | } 49 | 50 | func init() { 51 | RootCmd.AddCommand(swaggerCmd) 52 | 53 | // Here you will define your flags and configuration settings. 54 | 55 | // Cobra supports Persistent Flags which will work for this command 56 | // and all subcommands, e.g.: 57 | // swaggerCmd.PersistentFlags().String("foo", "", "A help for foo") 58 | 59 | // Cobra supports local flags which will only run when this command 60 | // is called directly, e.g.: 61 | // swaggerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") 62 | 63 | } 64 | 65 | func swaggerToAPI(swagger genswagger.Swagger) *design.APIDefinition { 66 | api := design.APIDefinition{ 67 | Host: swagger.Host, 68 | Schemes: swagger.Schemes, 69 | BasePath: swagger.BasePath, 70 | // Params *AttributeDefinition 71 | Consumes: []*design.EncodingDefinition{ 72 | &design.EncodingDefinition{ 73 | MIMETypes: swagger.Consumes, 74 | Encoder: false, 75 | }, 76 | }, 77 | Produces: []*design.EncodingDefinition{ 78 | &design.EncodingDefinition{ 79 | MIMETypes: swagger.Produces, 80 | Encoder: true, 81 | }, 82 | }, 83 | // Origins map[string]*CORSDefinition 84 | // Resources map[string]*ResourceDefinition 85 | // Types map[string]*UserTypeDefinition 86 | // MediaTypes map[string]*MediaTypeDefinition 87 | // Traits map[string]*dslengine.TraitDefinition 88 | // Responses map[string]*ResponseDefinition 89 | // ResponseTemplates map[string]*ResponseTemplateDefinition 90 | // DefaultResponses map[string]*ResponseDefinition 91 | // DefaultResponseTemplates map[string]*ResponseTemplateDefinition 92 | // DSLFunc func() 93 | // Metadata dslengine.MetadataDefinition 94 | // SecuritySchemes []*SecuritySchemeDefinition 95 | // Security *SecurityDefinition 96 | // NoExamples bool 97 | } 98 | if swagger.Info != nil { 99 | api.Name = swagger.Info.Title 100 | api.Title = swagger.Info.Title 101 | api.Description = swagger.Info.Description 102 | api.Version = swagger.Info.Version 103 | api.TermsOfService = swagger.Info.TermsOfService 104 | api.Contact = swagger.Info.Contact 105 | api.License = swagger.Info.License 106 | } 107 | if swagger.ExternalDocs != nil { 108 | api.Docs = &design.DocsDefinition{ 109 | Description: swagger.ExternalDocs.Description, 110 | URL: swagger.ExternalDocs.URL, 111 | } 112 | } 113 | return &api 114 | } 115 | -------------------------------------------------------------------------------- /cmd/template.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "sort" 5 | "text/template" 6 | 7 | "github.com/goadesign/goa/design" 8 | ) 9 | 10 | const ( 11 | goHeaderT = ` 12 | package design 13 | 14 | import ( 15 | . "github.com/goadesign/goa/design" 16 | . "github.com/goadesign/goa/design/apidsl" 17 | ) 18 | ` 19 | allT = ` 20 | {{template "goHeader" .}} 21 | {{template "api" .}} 22 | ` 23 | 24 | // Components that have single value. 25 | basePathT = `{{if .BasePath}}BasePath({{printf "%q" .BasePath}}){{end}}` // This template expects APIDefinition or ResourceDefinition. 26 | canonicalActionNameT = `{{if .CanonicalActionName}}CanonicalActionName({{printf "%q" .CanonicalActionName}}){{end}}` // This template expects ResourceDefinition. 27 | contentTypeT = `{{if .ContentType}}ContentType({{printf "%q" .ContentType}}){{end}}` // This template expects MediaTypeDefinition. 28 | credentialsT = `{{if .Credentials}}Credentials(){{end}}` // This template expects CORSDefinition. 29 | descriptionT = `{{if .Description}}Description({{printf "%q" .Description}}){{end}}` // This template expects APIDefinition or DocsDefinition or ResourceDefinition or ResponseDefinition. 30 | emailT = `{{if .Email}}Email({{printf "%q" .Email}}){{end}}` // This template expects ContactDefinition. 31 | functionT = `{{if .Function}}Function({{printf "%q" .Function}}){{end}}` // This template expects EncodingDefinition. 32 | hostT = `{{if .Host}}Host({{printf "%q" .Host}}){{end}}` // This template expects APIDefinition. 33 | maxAgeT = `{{if .MaxAge}}MaxAge({{.MaxAge}}){{end}}` // This template expects CORSDefinition. 34 | maxLengthT = `{{if .MaxLength}}MaxLength({{.MaxLength}}){{end}}` // This template expects ValidationDefinition. 35 | minLengthT = `{{if .MinLength}}MinLength({{.MinLength}}){{end}}` // This template expects ValidationDefinition. 36 | nameT = `{{if .Name}}Name({{printf "%q" .Name}}){{end}}` // This template expects APIDefinition or ContactDefinition or LicenseDefinition. 37 | packageT = `{{if .PackagePath}}Package({{printf "%q" .PackagePath}}){{end}}` // This template expects EncodingDefinition. 38 | statusT = `{{if .Status}}Status({{.Status}}){{end}}` // This template expects ResponseDefinition. 39 | termsOfServiceT = `{{if .TermsOfService}}TermsOfService({{printf "%q" .TermsOfService}}){{end}}` // This template expects APIDefinition. 40 | titleT = `{{if .Title}}Title({{printf "%q" .Title}}){{end}}` // This template expects APIDefinition. 41 | typeNameT = `{{if .TypeName}}TypeName({{printf "%q" .TypeName}}){{end}}` // This template expects MediaTypeDefinition or UserTypeDefinition. 42 | urlT = `{{if .URL}}URL({{printf "%q" .URL}}){{end}}` // This template expects ContactDefinition or LicenseDefinition or DocsDefinition. 43 | versionT = `{{if .Version}}Version({{printf "%q" .Version}}){{end}}` // This template expects APIDefinition. 44 | 45 | // Components that have multiple values. 46 | exposeT = `{{if .Exposed}}Expose({{if (eq (len .Exposed) 1)}}{{range .Exposed}}{{printf "%q" .}}{{end}}){{else}} 47 | {{range .Exposed}}{{printf "%q" .}}, 48 | {{end}}){{end}}{{end}}` // This template expects CORSDefinition. 49 | methodsT = `{{if .Methods}}Methods({{if (eq (len .Methods) 1)}}{{range .Methods}}{{printf "%q" .}}{{end}}){{else}} 50 | {{range .Methods}}{{printf "%q" .}}, 51 | {{end}}){{end}}{{end}}` // This template expects CORSDefinition. 52 | schemeT = `{{if .Schemes}}Scheme({{if (eq (len .Schemes) 1)}}{{range .Schemes}}{{printf "%q" .}}{{end}}){{else}} 53 | {{range .Schemes}}{{printf "%q" .}}, 54 | {{end}}){{end}}{{end}}` // This template expects APIDefinition or ResourceDefinition or ActionDefinition. 55 | 56 | // Containers. 57 | actionT = `{{if .Actions}}{{$actions := .Actions}}{{$keys := keys .Actions}}{{range $keys}}{{if (not (eq (index $keys 0) .))}} 58 | {{end}}{{with index $actions .}}Action({{printf "%q" .Name}}, func() { 59 | {{if .Description}}{{template "description" .}} 60 | {{end}}{{if .Docs}}{{template "docs" .}} 61 | {{end}}{{if .Schemes}}{{template "scheme" .}} 62 | {{end}}{{if .Routes}}{{template "routing" .}} 63 | {{end}}{{if .Payload}}{{template "payload" .}} 64 | {{end}}}){{end}}{{end}}{{end}}` 65 | apiT = `{{if .}}var _ = API({{if .Name}}{{printf "%q" .Name}}{{else}}""{{end}}, func() { 66 | {{if .Title}}{{template "title" .}} 67 | {{end}}{{if .Description}}{{template "description" .}} 68 | {{end}}{{if .Version}}{{template "version" .}} 69 | {{end}}{{if .TermsOfService}}{{template "termsOfService" .}} 70 | {{end}}{{if .Contact}}{{template "contact" .}} 71 | {{end}}{{if .License}}{{template "license" .}} 72 | {{end}}{{if .Docs}}{{template "docs" .}} 73 | {{end}}{{if .Host}}{{template "host" .}} 74 | {{end}}{{if .Schemes}}{{template "scheme" .}} 75 | {{end}}{{if .BasePath}}{{template "basePath" .}} 76 | {{end}}{{if .Origins}}{{template "origin" .}} 77 | {{end}}{{if .Consumes}}{{template "consumes" .}} 78 | {{end}}{{if .Produces}}{{template "produces" .}} 79 | {{end}}}){{end}}` 80 | connectT = `{{if and .Verb .Path}}{{if (eq .Verb "CONNECT")}}CONNECT({{printf "%q" .Path}}){{end}}{{end}}` 81 | consumesT = `{{if .Consumes}}{{range $index, $element := .Consumes}}{{with $element}}{{if (not (eq $index 0))}} 82 | {{end}}Consumes({{if (or (gt (len .MIMETypes) 1) .Function .PackagePath)}} 83 | {{range .MIMETypes}}{{printf "%q" .}}, 84 | {{end}}{{if (or .Function .PackagePath)}}func() { 85 | {{if .Function}}{{template "function" .}} 86 | {{end}}{{if .PackagePath}}{{template "package" .}} 87 | {{end}}}, 88 | {{end}}{{else}}{{range .MIMETypes}}{{printf "%q" .}}{{end}}{{end}}){{end}}{{end}}{{end}}` 89 | contactT = `{{if .Contact}}{{with .Contact}}Contact(func() { 90 | {{if .Name}}{{template "name" .}} 91 | {{end}}{{if .Email}}{{template "email" .}} 92 | {{end}}{{if .URL}}{{template "url" .}} 93 | {{end}}}){{end}}{{end}}` 94 | deleteT = `{{if and .Verb .Path}}{{if (eq .Verb "DELETE")}}DELETE({{printf "%q" .Path}}){{end}}{{end}}` 95 | docsT = `{{if .Docs}}{{with .Docs}}Docs(func() { 96 | {{if .Description}}{{template "description" .}} 97 | {{end}}{{if .URL}}{{template "url" .}} 98 | {{end}}}){{end}}{{end}}` 99 | getT = `{{if and .Verb .Path}}{{if (eq .Verb "GET")}}GET({{printf "%q" .Path}}){{end}}{{end}}` 100 | headT = `{{if and .Verb .Path}}{{if (eq .Verb "HEAD")}}HEAD({{printf "%q" .Path}}){{end}}{{end}}` 101 | licenseT = `{{if .License}}{{with .License}}License(func() { 102 | {{if .Name}}{{template "name" .}} 103 | {{end}}{{if .URL}}{{template "url" .}} 104 | {{end}}}){{end}}{{end}}` 105 | optionsT = `{{if and .Verb .Path}}{{if (eq .Verb "OPTIONS")}}OPTIONS({{printf "%q" .Path}}){{end}}{{end}}` 106 | originT = `{{if .Origins}}{{$origins := .Origins}}{{$keys := keys .Origins}}{{range $keys}}{{if (not (eq (index $keys 0) .))}} 107 | {{end}}{{with index $origins .}}Origin({{printf "%q" .Origin}}, func() { 108 | {{if .Headers}}Headers( 109 | {{range .Headers}}{{printf "%q" .}}, 110 | {{end}}) 111 | {{end}}{{if .Methods}}{{template "methods" .}} 112 | {{end}}{{if .Exposed}}{{template "expose" .}} 113 | {{end}}{{if .MaxAge}}{{template "maxAge" .}} 114 | {{end}}{{if .Credentials}}{{template "credentials" .}} 115 | {{end}}}){{end}}{{end}}{{end}}` 116 | patchT = `{{if and .Verb .Path}}{{if (eq .Verb "PATCH")}}PATCH({{printf "%q" .Path}}){{end}}{{end}}` 117 | payloadT = `{{if .Payload}}{{with .Payload}}Payload({{.TypeName}}){{end}}{{end}}` 118 | postT = `{{if and .Verb .Path}}{{if (eq .Verb "POST")}}POST({{printf "%q" .Path}}){{end}}{{end}}` 119 | putT = `{{if and .Verb .Path}}{{if (eq .Verb "PUT")}}PUT({{printf "%q" .Path}}){{end}}{{end}}` 120 | producesT = `{{if .Produces}}{{range $index, $element := .Produces}}{{with $element}}{{if (not (eq $index 0))}} 121 | {{end}}Produces({{if (or (gt (len .MIMETypes) 1) .Function .PackagePath)}} 122 | {{range .MIMETypes}}{{printf "%q" .}}, 123 | {{end}}{{if (or .Function .PackagePath)}}func() { 124 | {{if .Function}}{{template "function" .}} 125 | {{end}}{{if .PackagePath}}{{template "package" .}} 126 | {{end}}}, 127 | {{end}}{{else}}{{range .MIMETypes}}{{printf "%q" .}}{{end}}{{end}}){{end}}{{end}}{{end}}` 128 | responseT = `{{if .Responses}}{{$responses := .Responses}}{{$keys := keys .Responses}}{{range $keys}}{{if (not (eq (index $keys 0) .))}} 129 | {{end}}{{with index $responses .}}Response({{.Name}}){{end}}{{end}}{{end}}` 130 | routingT = `{{if .Routes}}Routing({{if (eq (len .Routes) 1)}}{{range .Routes}}{{template "connect" .}}{{template "delete" .}}{{template "get" .}}{{template "head" .}}{{template "options" .}}{{template "patch" .}}{{template "post" .}}{{template "put" .}}{{template "trace" .}}{{end}}){{else}} 131 | {{range .Routes}}{{if (eq .Verb "CONNECT")}}{{template "connect" .}}, 132 | {{end}}{{if (eq .Verb "DELETE")}}{{template "delete" .}}, 133 | {{end}}{{if (eq .Verb "GET")}}{{template "get" .}}, 134 | {{end}}{{if (eq .Verb "HEAD")}}{{template "head" .}}, 135 | {{end}}{{if (eq .Verb "OPTIONS")}}{{template "options" .}}, 136 | {{end}}{{if (eq .Verb "PATCH")}}{{template "patch" .}}, 137 | {{end}}{{if (eq .Verb "POST")}}{{template "post" .}}, 138 | {{end}}{{if (eq .Verb "PUT")}}{{template "put" .}}, 139 | {{end}}{{if (eq .Verb "TRACE")}}{{template "trace" .}}, 140 | {{end}}{{end}}){{end}}{{end}}` 141 | traceT = `{{if and .Verb .Path}}{{if (eq .Verb "TRACE")}}TRACE({{printf "%q" .Path}}){{end}}{{end}}` 142 | typeT = `{{if .Types}}{{$types := .Types}}{{$keys := keys .Types}}{{range $keys}}{{if (not (eq (index $keys 0) .))}} 143 | {{end}}{{with index $types .}}var {{.TypeName}} = Type({{printf "%q" .TypeName}}, func() { 144 | }){{end}}{{end}}{{end}}` 145 | ) 146 | 147 | var ( 148 | tmpl *template.Template 149 | ) 150 | 151 | func init() { 152 | tmpl = template.New("") 153 | 154 | tmpl = tmpl.Funcs(template.FuncMap{ 155 | "keys": func(x interface{}) interface{} { 156 | switch t := x.(type) { 157 | case map[string]*design.ActionDefinition: 158 | var keys []string 159 | for k := range t { 160 | keys = append(keys, k) 161 | } 162 | sort.Strings(keys) 163 | return keys 164 | case map[string]*design.CORSDefinition: 165 | var keys []string 166 | for k := range t { 167 | keys = append(keys, k) 168 | } 169 | sort.Strings(keys) 170 | return keys 171 | case map[string]*design.ResponseDefinition: 172 | var keys []string 173 | for k := range t { 174 | keys = append(keys, k) 175 | } 176 | sort.Strings(keys) 177 | return keys 178 | case map[string]*design.UserTypeDefinition: 179 | var keys []string 180 | for k := range t { 181 | keys = append(keys, k) 182 | } 183 | sort.Strings(keys) 184 | return keys 185 | default: 186 | return nil 187 | } 188 | }, 189 | }) 190 | 191 | tmpl = template.Must(tmpl.New("goHeader").Parse(goHeaderT)) 192 | tmpl = template.Must(tmpl.New("all").Parse(allT)) 193 | 194 | // Components that have single value. 195 | tmpl = template.Must(tmpl.New("basePath").Parse(basePathT)) 196 | tmpl = template.Must(tmpl.New("canonicalActionName").Parse(canonicalActionNameT)) 197 | tmpl = template.Must(tmpl.New("contentType").Parse(contentTypeT)) 198 | tmpl = template.Must(tmpl.New("credentials").Parse(credentialsT)) 199 | tmpl = template.Must(tmpl.New("description").Parse(descriptionT)) 200 | tmpl = template.Must(tmpl.New("email").Parse(emailT)) 201 | tmpl = template.Must(tmpl.New("function").Parse(functionT)) 202 | tmpl = template.Must(tmpl.New("host").Parse(hostT)) 203 | tmpl = template.Must(tmpl.New("maxAge").Parse(maxAgeT)) 204 | tmpl = template.Must(tmpl.New("maxLength").Parse(maxLengthT)) 205 | tmpl = template.Must(tmpl.New("minLength").Parse(minLengthT)) 206 | tmpl = template.Must(tmpl.New("name").Parse(nameT)) 207 | tmpl = template.Must(tmpl.New("package").Parse(packageT)) 208 | tmpl = template.Must(tmpl.New("status").Parse(statusT)) 209 | tmpl = template.Must(tmpl.New("termsOfService").Parse(termsOfServiceT)) 210 | tmpl = template.Must(tmpl.New("title").Parse(titleT)) 211 | tmpl = template.Must(tmpl.New("typeName").Parse(typeNameT)) 212 | tmpl = template.Must(tmpl.New("url").Parse(urlT)) 213 | tmpl = template.Must(tmpl.New("version").Parse(versionT)) 214 | 215 | // Components that have multiple values. 216 | tmpl = template.Must(tmpl.New("expose").Parse(exposeT)) 217 | tmpl = template.Must(tmpl.New("methods").Parse(methodsT)) 218 | tmpl = template.Must(tmpl.New("scheme").Parse(schemeT)) 219 | 220 | // Containers. 221 | tmpl = template.Must(tmpl.New("action").Parse(actionT)) 222 | tmpl = template.Must(tmpl.New("api").Parse(apiT)) 223 | tmpl = template.Must(tmpl.New("connect").Parse(connectT)) 224 | tmpl = template.Must(tmpl.New("consumes").Parse(consumesT)) 225 | tmpl = template.Must(tmpl.New("contact").Parse(contactT)) 226 | tmpl = template.Must(tmpl.New("delete").Parse(deleteT)) 227 | tmpl = template.Must(tmpl.New("docs").Parse(docsT)) 228 | tmpl = template.Must(tmpl.New("get").Parse(getT)) 229 | tmpl = template.Must(tmpl.New("head").Parse(headT)) 230 | tmpl = template.Must(tmpl.New("license").Parse(licenseT)) 231 | tmpl = template.Must(tmpl.New("options").Parse(optionsT)) 232 | tmpl = template.Must(tmpl.New("origin").Parse(originT)) 233 | tmpl = template.Must(tmpl.New("patch").Parse(patchT)) 234 | tmpl = template.Must(tmpl.New("payload").Parse(payloadT)) 235 | tmpl = template.Must(tmpl.New("post").Parse(postT)) 236 | tmpl = template.Must(tmpl.New("put").Parse(putT)) 237 | tmpl = template.Must(tmpl.New("produces").Parse(producesT)) 238 | tmpl = template.Must(tmpl.New("response").Parse(responseT)) 239 | tmpl = template.Must(tmpl.New("routing").Parse(routingT)) 240 | tmpl = template.Must(tmpl.New("trace").Parse(traceT)) 241 | tmpl = template.Must(tmpl.New("type").Parse(typeT)) 242 | } 243 | -------------------------------------------------------------------------------- /cmd/template_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/goadesign/goa/design" 8 | "github.com/goadesign/goa/dslengine" 9 | ) 10 | 11 | func TestGoHeaderTmpl(t *testing.T) { 12 | cases := map[string]struct { 13 | expected string 14 | }{ 15 | "pattern 1": { 16 | expected: ` 17 | package design 18 | 19 | import ( 20 | . "github.com/goadesign/goa/design" 21 | . "github.com/goadesign/goa/design/apidsl" 22 | ) 23 | `, 24 | }, 25 | } 26 | for k, tc := range cases { 27 | buf := new(bytes.Buffer) 28 | if err := tmpl.ExecuteTemplate(buf, "goHeader", nil); err != nil { 29 | t.Fatalf("Execute returned %s", err) 30 | } 31 | actual := buf.String() 32 | if actual != tc.expected { 33 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 34 | } 35 | } 36 | } 37 | 38 | func TestAllTmpl(t *testing.T) { 39 | // TODO Write. 40 | } 41 | 42 | // Components have single values. 43 | func TestBasePathTmpl(t *testing.T) { 44 | cases := map[string]struct { 45 | definition interface{} 46 | expected string 47 | }{ 48 | "with api definition": { 49 | definition: design.APIDefinition{ 50 | BasePath: "/v1", 51 | }, 52 | expected: `BasePath("/v1")`, 53 | }, 54 | "with resource definition": { 55 | definition: design.ResourceDefinition{ 56 | BasePath: "/resource", 57 | }, 58 | expected: `BasePath("/resource")`, 59 | }, 60 | "without definition": { 61 | definition: design.APIDefinition{}, 62 | expected: ``, 63 | }, 64 | } 65 | for k, tc := range cases { 66 | buf := new(bytes.Buffer) 67 | if err := tmpl.ExecuteTemplate(buf, "basePath", tc.definition); err != nil { 68 | t.Fatalf("Execute returned %s", err) 69 | } 70 | actual := buf.String() 71 | if actual != tc.expected { 72 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 73 | } 74 | } 75 | } 76 | 77 | func TestCanonicalActionNameTmpl(t *testing.T) { 78 | cases := map[string]struct { 79 | definition interface{} 80 | expected string 81 | }{ 82 | "with definition": { 83 | definition: design.ResourceDefinition{ 84 | CanonicalActionName: "get", 85 | }, 86 | expected: `CanonicalActionName("get")`, 87 | }, 88 | "without definition": { 89 | definition: design.ResourceDefinition{}, 90 | expected: ``, 91 | }, 92 | } 93 | for k, tc := range cases { 94 | buf := new(bytes.Buffer) 95 | if err := tmpl.ExecuteTemplate(buf, "canonicalActionName", tc.definition); err != nil { 96 | t.Fatalf("Execute returned %s", err) 97 | } 98 | actual := buf.String() 99 | if actual != tc.expected { 100 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 101 | } 102 | } 103 | } 104 | 105 | func TestContentTypeTmpl(t *testing.T) { 106 | cases := map[string]struct { 107 | definition interface{} 108 | expected string 109 | }{ 110 | "with definition": { 111 | definition: design.MediaTypeDefinition{ 112 | ContentType: "application/json", 113 | }, 114 | expected: `ContentType("application/json")`, 115 | }, 116 | "without definition": { 117 | definition: design.MediaTypeDefinition{}, 118 | expected: ``, 119 | }, 120 | } 121 | for k, tc := range cases { 122 | buf := new(bytes.Buffer) 123 | if err := tmpl.ExecuteTemplate(buf, "contentType", tc.definition); err != nil { 124 | t.Fatalf("Execute returned %s", err) 125 | } 126 | actual := buf.String() 127 | if actual != tc.expected { 128 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 129 | } 130 | } 131 | } 132 | 133 | func TestCredentialsTmpl(t *testing.T) { 134 | cases := map[string]struct { 135 | definition interface{} 136 | expected string 137 | }{ 138 | "with definition": { 139 | definition: design.CORSDefinition{ 140 | Credentials: true, 141 | }, 142 | expected: `Credentials()`, 143 | }, 144 | "without definition": { 145 | definition: design.CORSDefinition{}, 146 | expected: ``, 147 | }, 148 | } 149 | for k, tc := range cases { 150 | buf := new(bytes.Buffer) 151 | if err := tmpl.ExecuteTemplate(buf, "credentials", tc.definition); err != nil { 152 | t.Fatalf("Execute returned %s", err) 153 | } 154 | actual := buf.String() 155 | if actual != tc.expected { 156 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 157 | } 158 | } 159 | } 160 | 161 | func TestDescriptionTmpl(t *testing.T) { 162 | cases := map[string]struct { 163 | definition interface{} 164 | expected string 165 | }{ 166 | "with api definition": { 167 | definition: design.APIDefinition{ 168 | Description: "Description of API", 169 | }, 170 | expected: `Description("Description of API")`, 171 | }, 172 | "with docs definition": { 173 | definition: design.DocsDefinition{ 174 | Description: "Description of docs", 175 | }, 176 | expected: `Description("Description of docs")`, 177 | }, 178 | "with resource definition": { 179 | definition: design.ResourceDefinition{ 180 | Description: "Description of resource", 181 | }, 182 | expected: `Description("Description of resource")`, 183 | }, 184 | "with response definition": { 185 | definition: design.ResponseDefinition{ 186 | Description: "Description of response", 187 | }, 188 | expected: `Description("Description of response")`, 189 | }, 190 | "with multi lines in definition": { 191 | definition: design.APIDefinition{ 192 | Description: `Description 193 | of 194 | API`, 195 | }, 196 | expected: `Description("Description\nof\nAPI")`, 197 | }, 198 | "without definition": { 199 | definition: design.APIDefinition{}, 200 | expected: ``, 201 | }, 202 | } 203 | for k, tc := range cases { 204 | buf := new(bytes.Buffer) 205 | if err := tmpl.ExecuteTemplate(buf, "description", tc.definition); err != nil { 206 | t.Fatalf("Execute returned %s", err) 207 | } 208 | actual := buf.String() 209 | if actual != tc.expected { 210 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 211 | } 212 | } 213 | } 214 | 215 | func TestEmailTmpl(t *testing.T) { 216 | cases := map[string]struct { 217 | definition interface{} 218 | expected string 219 | }{ 220 | "with definition": { 221 | definition: design.ContactDefinition{ 222 | Email: "foo@bar.com", 223 | }, 224 | expected: `Email("foo@bar.com")`, 225 | }, 226 | "without definition": { 227 | definition: design.ContactDefinition{}, 228 | expected: ``, 229 | }, 230 | } 231 | for k, tc := range cases { 232 | buf := new(bytes.Buffer) 233 | if err := tmpl.ExecuteTemplate(buf, "email", tc.definition); err != nil { 234 | t.Fatalf("Execute returned %s", err) 235 | } 236 | actual := buf.String() 237 | if actual != tc.expected { 238 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 239 | } 240 | } 241 | } 242 | 243 | func TestFunctionTmpl(t *testing.T) { 244 | cases := map[string]struct { 245 | definition interface{} 246 | expected string 247 | }{ 248 | "with definition": { 249 | definition: design.EncodingDefinition{ 250 | Function: "newFunc", 251 | }, 252 | expected: `Function("newFunc")`, 253 | }, 254 | "without definition": { 255 | definition: design.EncodingDefinition{}, 256 | expected: ``, 257 | }, 258 | } 259 | for k, tc := range cases { 260 | buf := new(bytes.Buffer) 261 | if err := tmpl.ExecuteTemplate(buf, "function", tc.definition); err != nil { 262 | t.Fatalf("Execute returned %s", err) 263 | } 264 | actual := buf.String() 265 | if actual != tc.expected { 266 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 267 | } 268 | } 269 | } 270 | 271 | func TestHostTmpl(t *testing.T) { 272 | cases := map[string]struct { 273 | definition interface{} 274 | expected string 275 | }{ 276 | "with definition": { 277 | definition: design.APIDefinition{ 278 | Host: "http://localhost/", 279 | }, 280 | expected: `Host("http://localhost/")`, 281 | }, 282 | "without definition": { 283 | definition: design.APIDefinition{}, 284 | expected: ``, 285 | }, 286 | } 287 | for k, tc := range cases { 288 | buf := new(bytes.Buffer) 289 | if err := tmpl.ExecuteTemplate(buf, "host", tc.definition); err != nil { 290 | t.Fatalf("Execute returned %s", err) 291 | } 292 | actual := buf.String() 293 | if actual != tc.expected { 294 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 295 | } 296 | } 297 | } 298 | 299 | func TestMaxAgeTmpl(t *testing.T) { 300 | cases := map[string]struct { 301 | definition interface{} 302 | expected string 303 | }{ 304 | "with definition": { 305 | definition: design.CORSDefinition{ 306 | MaxAge: 600, 307 | }, 308 | expected: `MaxAge(600)`, 309 | }, 310 | "without definition": { 311 | definition: design.CORSDefinition{}, 312 | expected: ``, 313 | }, 314 | } 315 | for k, tc := range cases { 316 | buf := new(bytes.Buffer) 317 | if err := tmpl.ExecuteTemplate(buf, "maxAge", tc.definition); err != nil { 318 | t.Fatalf("Execute returned %s", err) 319 | } 320 | actual := buf.String() 321 | if actual != tc.expected { 322 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 323 | } 324 | } 325 | } 326 | 327 | func TestMaxLengthTmpl(t *testing.T) { 328 | cases := map[string]struct { 329 | definition interface{} 330 | expected string 331 | }{ 332 | "with definition": { 333 | definition: dslengine.ValidationDefinition{ 334 | MaxLength: &[]int{5}[0], 335 | }, 336 | expected: `MaxLength(5)`, 337 | }, 338 | "without definition": { 339 | definition: dslengine.ValidationDefinition{}, 340 | expected: ``, 341 | }, 342 | } 343 | for k, tc := range cases { 344 | buf := new(bytes.Buffer) 345 | if err := tmpl.ExecuteTemplate(buf, "maxLength", tc.definition); err != nil { 346 | t.Fatalf("Execute returned %s", err) 347 | } 348 | actual := buf.String() 349 | if actual != tc.expected { 350 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 351 | } 352 | } 353 | } 354 | 355 | func TestMinLengthTmpl(t *testing.T) { 356 | cases := map[string]struct { 357 | definition interface{} 358 | expected string 359 | }{ 360 | "with definition": { 361 | definition: dslengine.ValidationDefinition{ 362 | MinLength: &[]int{3}[0], 363 | }, 364 | expected: `MinLength(3)`, 365 | }, 366 | "without definition": { 367 | definition: dslengine.ValidationDefinition{}, 368 | expected: ``, 369 | }, 370 | } 371 | for k, tc := range cases { 372 | buf := new(bytes.Buffer) 373 | if err := tmpl.ExecuteTemplate(buf, "minLength", tc.definition); err != nil { 374 | t.Fatalf("Execute returned %s", err) 375 | } 376 | actual := buf.String() 377 | if actual != tc.expected { 378 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 379 | } 380 | } 381 | } 382 | 383 | func TestNameTmpl(t *testing.T) { 384 | cases := map[string]struct { 385 | definition interface{} 386 | expected string 387 | }{ 388 | "with api definition": { 389 | definition: design.APIDefinition{ 390 | Name: "Name of API", 391 | }, 392 | expected: `Name("Name of API")`, 393 | }, 394 | "with contact definition": { 395 | definition: design.ContactDefinition{ 396 | Name: "Name of contact", 397 | }, 398 | expected: `Name("Name of contact")`, 399 | }, 400 | "with license definition": { 401 | definition: design.LicenseDefinition{ 402 | Name: "Name of license", 403 | }, 404 | expected: `Name("Name of license")`, 405 | }, 406 | "without definition": { 407 | definition: design.ContactDefinition{}, 408 | expected: ``, 409 | }, 410 | } 411 | for k, tc := range cases { 412 | buf := new(bytes.Buffer) 413 | if err := tmpl.ExecuteTemplate(buf, "name", tc.definition); err != nil { 414 | t.Fatalf("Execute returned %s", err) 415 | } 416 | actual := buf.String() 417 | if actual != tc.expected { 418 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 419 | } 420 | } 421 | } 422 | 423 | func TestPackageTmpl(t *testing.T) { 424 | cases := map[string]struct { 425 | definition interface{} 426 | expected string 427 | }{ 428 | "with definition": { 429 | definition: design.EncodingDefinition{ 430 | PackagePath: "encoding/json", 431 | }, 432 | expected: `Package("encoding/json")`, 433 | }, 434 | "without definition": { 435 | definition: design.EncodingDefinition{}, 436 | expected: ``, 437 | }, 438 | } 439 | for k, tc := range cases { 440 | buf := new(bytes.Buffer) 441 | if err := tmpl.ExecuteTemplate(buf, "package", tc.definition); err != nil { 442 | t.Fatalf("Execute returned %s", err) 443 | } 444 | actual := buf.String() 445 | if actual != tc.expected { 446 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 447 | } 448 | } 449 | } 450 | 451 | func TestStatusTmpl(t *testing.T) { 452 | cases := map[string]struct { 453 | definition interface{} 454 | expected string 455 | }{ 456 | "with definition": { 457 | definition: design.ResponseDefinition{ 458 | Status: 200, 459 | }, 460 | expected: `Status(200)`, 461 | }, 462 | "without definition": { 463 | definition: design.ResponseDefinition{}, 464 | expected: ``, 465 | }, 466 | } 467 | for k, tc := range cases { 468 | buf := new(bytes.Buffer) 469 | if err := tmpl.ExecuteTemplate(buf, "status", tc.definition); err != nil { 470 | t.Fatalf("Execute returned %s", err) 471 | } 472 | actual := buf.String() 473 | if actual != tc.expected { 474 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 475 | } 476 | } 477 | } 478 | 479 | func TestTermsOfServiceTmpl(t *testing.T) { 480 | cases := map[string]struct { 481 | definition interface{} 482 | expected string 483 | }{ 484 | "with definition": { 485 | definition: design.APIDefinition{ 486 | TermsOfService: "terms of service", 487 | }, 488 | expected: `TermsOfService("terms of service")`, 489 | }, 490 | "without definition": { 491 | definition: design.APIDefinition{}, 492 | expected: ``, 493 | }, 494 | } 495 | for k, tc := range cases { 496 | buf := new(bytes.Buffer) 497 | if err := tmpl.ExecuteTemplate(buf, "termsOfService", tc.definition); err != nil { 498 | t.Fatalf("Execute returned %s", err) 499 | } 500 | actual := buf.String() 501 | if actual != tc.expected { 502 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 503 | } 504 | } 505 | } 506 | 507 | func TestTitleTmpl(t *testing.T) { 508 | cases := map[string]struct { 509 | definition interface{} 510 | expected string 511 | }{ 512 | "with definition": { 513 | definition: design.APIDefinition{ 514 | Title: "Title of API", 515 | }, 516 | expected: `Title("Title of API")`, 517 | }, 518 | "without definition": { 519 | definition: design.APIDefinition{}, 520 | expected: ``, 521 | }, 522 | } 523 | for k, tc := range cases { 524 | buf := new(bytes.Buffer) 525 | if err := tmpl.ExecuteTemplate(buf, "title", tc.definition); err != nil { 526 | t.Fatalf("Execute returned %s", err) 527 | } 528 | actual := buf.String() 529 | if actual != tc.expected { 530 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 531 | } 532 | } 533 | } 534 | 535 | func TestTypeNameTmpl(t *testing.T) { 536 | cases := map[string]struct { 537 | definition interface{} 538 | expected string 539 | }{ 540 | "with media type definition": { 541 | definition: design.MediaTypeDefinition{ 542 | UserTypeDefinition: &design.UserTypeDefinition{ 543 | TypeName: "TypeName of media type", 544 | }, 545 | }, 546 | expected: `TypeName("TypeName of media type")`, 547 | }, 548 | "with user type definition": { 549 | definition: design.UserTypeDefinition{ 550 | TypeName: "TypeName of user type", 551 | }, 552 | expected: `TypeName("TypeName of user type")`, 553 | }, 554 | "without definition": { 555 | definition: design.UserTypeDefinition{}, 556 | expected: ``, 557 | }, 558 | } 559 | for k, tc := range cases { 560 | buf := new(bytes.Buffer) 561 | if err := tmpl.ExecuteTemplate(buf, "typeName", tc.definition); err != nil { 562 | t.Fatalf("Execute returned %s", err) 563 | } 564 | actual := buf.String() 565 | if actual != tc.expected { 566 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 567 | } 568 | } 569 | } 570 | 571 | func TestURLTmpl(t *testing.T) { 572 | cases := map[string]struct { 573 | definition interface{} 574 | expected string 575 | }{ 576 | "with contact definition": { 577 | definition: design.ContactDefinition{ 578 | URL: "http://localhost/contact", 579 | }, 580 | expected: `URL("http://localhost/contact")`, 581 | }, 582 | "with license definition": { 583 | definition: design.LicenseDefinition{ 584 | URL: "http://localhost/license", 585 | }, 586 | expected: `URL("http://localhost/license")`, 587 | }, 588 | "with docs definition": { 589 | definition: design.DocsDefinition{ 590 | URL: "http://localhost/docs", 591 | }, 592 | expected: `URL("http://localhost/docs")`, 593 | }, 594 | "without definition": { 595 | definition: design.ContactDefinition{}, 596 | expected: ``, 597 | }, 598 | } 599 | for k, tc := range cases { 600 | buf := new(bytes.Buffer) 601 | if err := tmpl.ExecuteTemplate(buf, "url", tc.definition); err != nil { 602 | t.Fatalf("Execute returned %s", err) 603 | } 604 | actual := buf.String() 605 | if actual != tc.expected { 606 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 607 | } 608 | } 609 | } 610 | 611 | func TestVersionTmpl(t *testing.T) { 612 | cases := map[string]struct { 613 | definition interface{} 614 | expected string 615 | }{ 616 | "with definition": { 617 | definition: design.APIDefinition{ 618 | Version: "1.0.0", 619 | }, 620 | expected: `Version("1.0.0")`, 621 | }, 622 | "without definition": { 623 | definition: design.APIDefinition{}, 624 | expected: ``, 625 | }, 626 | } 627 | for k, tc := range cases { 628 | buf := new(bytes.Buffer) 629 | if err := tmpl.ExecuteTemplate(buf, "version", tc.definition); err != nil { 630 | t.Fatalf("Execute returned %s", err) 631 | } 632 | actual := buf.String() 633 | if actual != tc.expected { 634 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 635 | } 636 | } 637 | } 638 | 639 | // Components have multiple values. 640 | func TestExposeTmpl(t *testing.T) { 641 | cases := map[string]struct { 642 | definition interface{} 643 | expected string 644 | }{ 645 | "with single definition": { 646 | definition: design.CORSDefinition{ 647 | Exposed: []string{ 648 | "Authorization", 649 | }, 650 | }, 651 | expected: `Expose("Authorization")`, 652 | }, 653 | "with multi definition": { 654 | definition: design.CORSDefinition{ 655 | Exposed: []string{ 656 | "Authorization", 657 | "Link", 658 | }, 659 | }, 660 | expected: `Expose( 661 | "Authorization", 662 | "Link", 663 | )`, 664 | }, 665 | "without definition": { 666 | definition: design.CORSDefinition{}, 667 | expected: ``, 668 | }, 669 | } 670 | for k, tc := range cases { 671 | buf := new(bytes.Buffer) 672 | if err := tmpl.ExecuteTemplate(buf, "expose", tc.definition); err != nil { 673 | t.Fatalf("Execute returned %s", err) 674 | } 675 | actual := buf.String() 676 | if actual != tc.expected { 677 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 678 | } 679 | } 680 | } 681 | 682 | func TestMethodsTmpl(t *testing.T) { 683 | cases := map[string]struct { 684 | definition interface{} 685 | expected string 686 | }{ 687 | "with single definition": { 688 | definition: design.CORSDefinition{ 689 | Methods: []string{ 690 | "GET", 691 | }, 692 | }, 693 | expected: `Methods("GET")`, 694 | }, 695 | "with multi definition": { 696 | definition: design.CORSDefinition{ 697 | Methods: []string{ 698 | "GET", 699 | "POST", 700 | }, 701 | }, 702 | expected: `Methods( 703 | "GET", 704 | "POST", 705 | )`, 706 | }, 707 | "without definition": { 708 | definition: design.CORSDefinition{}, 709 | expected: ``, 710 | }, 711 | } 712 | for k, tc := range cases { 713 | buf := new(bytes.Buffer) 714 | if err := tmpl.ExecuteTemplate(buf, "methods", tc.definition); err != nil { 715 | t.Fatalf("Execute returned %s", err) 716 | } 717 | actual := buf.String() 718 | if actual != tc.expected { 719 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 720 | } 721 | } 722 | } 723 | 724 | func TestSchemeTmpl(t *testing.T) { 725 | cases := map[string]struct { 726 | definition interface{} 727 | expected string 728 | }{ 729 | "with api definition": { 730 | definition: design.APIDefinition{ 731 | Schemes: []string{ 732 | "http", 733 | }, 734 | }, 735 | expected: `Scheme("http")`, 736 | }, 737 | "with resource definition": { 738 | definition: design.ResourceDefinition{ 739 | Schemes: []string{ 740 | "http", 741 | }, 742 | }, 743 | expected: `Scheme("http")`, 744 | }, 745 | "with action definition": { 746 | definition: design.ActionDefinition{ 747 | Schemes: []string{ 748 | "http", 749 | }, 750 | }, 751 | expected: `Scheme("http")`, 752 | }, 753 | "with multi definition": { 754 | definition: design.APIDefinition{ 755 | Schemes: []string{ 756 | "http", 757 | "https", 758 | }, 759 | }, 760 | expected: `Scheme( 761 | "http", 762 | "https", 763 | )`, 764 | }, 765 | "without definition": { 766 | definition: design.APIDefinition{}, 767 | expected: ``, 768 | }, 769 | } 770 | for k, tc := range cases { 771 | buf := new(bytes.Buffer) 772 | if err := tmpl.ExecuteTemplate(buf, "scheme", tc.definition); err != nil { 773 | t.Fatalf("Execute returned %s", err) 774 | } 775 | actual := buf.String() 776 | if actual != tc.expected { 777 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 778 | } 779 | } 780 | } 781 | 782 | // Containers. 783 | func TestActionTmpl(t *testing.T) { 784 | cases := map[string]struct { 785 | definition interface{} 786 | expected string 787 | }{ 788 | "with single definition": { 789 | definition: design.ResourceDefinition{ 790 | Actions: map[string]*design.ActionDefinition{ 791 | "": &design.ActionDefinition{ 792 | Description: "Description of action", 793 | Docs: &design.DocsDefinition{ 794 | Description: "Description of docs", 795 | URL: "http://localhost/docs", 796 | }, 797 | Schemes: []string{ 798 | "http", 799 | }, 800 | Routes: []*design.RouteDefinition{ 801 | &design.RouteDefinition{ 802 | Verb: "GET", 803 | Path: "/", 804 | }, 805 | }, 806 | Payload: &design.UserTypeDefinition{ 807 | TypeName: "FooPayload", 808 | }, 809 | }, 810 | }, 811 | }, 812 | expected: `Action("", func() { 813 | Description("Description of action") 814 | Docs(func() { 815 | Description("Description of docs") 816 | URL("http://localhost/docs") 817 | }) 818 | Scheme("http") 819 | Routing(GET("/")) 820 | Payload(FooPayload) 821 | })`, 822 | }, 823 | "without definition": { 824 | definition: design.ResourceDefinition{}, 825 | expected: ``, 826 | }, 827 | } 828 | for k, tc := range cases { 829 | buf := new(bytes.Buffer) 830 | if err := tmpl.ExecuteTemplate(buf, "action", tc.definition); err != nil { 831 | t.Fatalf("Execute returned %s", err) 832 | } 833 | actual := buf.String() 834 | if actual != tc.expected { 835 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 836 | } 837 | } 838 | } 839 | 840 | func TestAPITmpl(t *testing.T) { 841 | cases := map[string]struct { 842 | definition interface{} 843 | expected string 844 | }{ 845 | "with definition": { 846 | definition: design.APIDefinition{ 847 | Name: "Name of API", 848 | Title: "Title of API", 849 | Description: "Description of API", 850 | Version: "1.0.0", 851 | TermsOfService: "terms of service", 852 | Contact: &design.ContactDefinition{ 853 | Name: "Name of contact", 854 | Email: "foo@bar.com", 855 | URL: "http://localhost/contact", 856 | }, 857 | License: &design.LicenseDefinition{ 858 | Name: "Name of license", 859 | URL: "http://localhost/license", 860 | }, 861 | Docs: &design.DocsDefinition{ 862 | Description: "Description of docs", 863 | URL: "http://localhost/docs", 864 | }, 865 | Host: "http://localhost/", 866 | Schemes: []string{ 867 | "http", 868 | "https", 869 | }, 870 | BasePath: "/v1", 871 | Origins: map[string]*design.CORSDefinition{ 872 | "*": &design.CORSDefinition{ 873 | Origin: "*", 874 | Headers: []string{ 875 | "Authorization", 876 | "Link", 877 | }, 878 | Methods: []string{ 879 | "GET", 880 | }, 881 | Exposed: []string{ 882 | "Authorization", 883 | }, 884 | MaxAge: 600, 885 | Credentials: true, 886 | Regexp: false, 887 | }, 888 | }, 889 | Consumes: []*design.EncodingDefinition{ 890 | &design.EncodingDefinition{ 891 | MIMETypes: []string{"application/json"}, 892 | Encoder: false, 893 | }, 894 | }, 895 | Produces: []*design.EncodingDefinition{ 896 | &design.EncodingDefinition{ 897 | MIMETypes: []string{"application/json"}, 898 | Encoder: true, 899 | }, 900 | }, 901 | }, 902 | expected: `var _ = API("Name of API", func() { 903 | Title("Title of API") 904 | Description("Description of API") 905 | Version("1.0.0") 906 | TermsOfService("terms of service") 907 | Contact(func() { 908 | Name("Name of contact") 909 | Email("foo@bar.com") 910 | URL("http://localhost/contact") 911 | }) 912 | License(func() { 913 | Name("Name of license") 914 | URL("http://localhost/license") 915 | }) 916 | Docs(func() { 917 | Description("Description of docs") 918 | URL("http://localhost/docs") 919 | }) 920 | Host("http://localhost/") 921 | Scheme( 922 | "http", 923 | "https", 924 | ) 925 | BasePath("/v1") 926 | Origin("*", func() { 927 | Headers( 928 | "Authorization", 929 | "Link", 930 | ) 931 | Methods("GET") 932 | Expose("Authorization") 933 | MaxAge(600) 934 | Credentials() 935 | }) 936 | Consumes("application/json") 937 | Produces("application/json") 938 | })`, 939 | }, 940 | "without definition": { 941 | definition: design.APIDefinition{}, 942 | expected: `var _ = API("", func() { 943 | })`, 944 | }, 945 | } 946 | for k, tc := range cases { 947 | buf := new(bytes.Buffer) 948 | if err := tmpl.ExecuteTemplate(buf, "api", tc.definition); err != nil { 949 | t.Fatalf("Execute returned %s", err) 950 | } 951 | actual := buf.String() 952 | if actual != tc.expected { 953 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 954 | } 955 | } 956 | } 957 | 958 | func TestCONNECTTmpl(t *testing.T) { 959 | cases := map[string]struct { 960 | definition interface{} 961 | expected string 962 | }{ 963 | "with connect definition": { 964 | definition: design.RouteDefinition{ 965 | Verb: "CONNECT", 966 | Path: "/", 967 | }, 968 | expected: `CONNECT("/")`, 969 | }, 970 | "with get definition": { 971 | definition: design.RouteDefinition{ 972 | Verb: "GET", 973 | Path: "/", 974 | }, 975 | expected: ``, 976 | }, 977 | "without definition": { 978 | definition: design.RouteDefinition{}, 979 | expected: ``, 980 | }, 981 | } 982 | for k, tc := range cases { 983 | buf := new(bytes.Buffer) 984 | if err := tmpl.ExecuteTemplate(buf, "connect", tc.definition); err != nil { 985 | t.Fatalf("Execute returned %s", err) 986 | } 987 | actual := buf.String() 988 | if actual != tc.expected { 989 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 990 | } 991 | } 992 | } 993 | 994 | func TestConsumesTmpl(t *testing.T) { 995 | cases := map[string]struct { 996 | definition interface{} 997 | expected string 998 | }{ 999 | "with complex definition": { 1000 | definition: design.APIDefinition{ 1001 | Consumes: []*design.EncodingDefinition{ 1002 | &design.EncodingDefinition{ 1003 | MIMETypes: []string{"application/json"}, 1004 | Encoder: false, 1005 | }, 1006 | &design.EncodingDefinition{ 1007 | MIMETypes: []string{ 1008 | "application/json", 1009 | "application/xml", 1010 | }, 1011 | Encoder: false, 1012 | }, 1013 | &design.EncodingDefinition{ 1014 | MIMETypes: []string{"application/json"}, 1015 | Function: "newFunc", 1016 | Encoder: false, 1017 | }, 1018 | &design.EncodingDefinition{ 1019 | MIMETypes: []string{"application/json"}, 1020 | PackagePath: "encoding/json", 1021 | Encoder: false, 1022 | }, 1023 | }, 1024 | }, 1025 | expected: `Consumes("application/json") 1026 | Consumes( 1027 | "application/json", 1028 | "application/xml", 1029 | ) 1030 | Consumes( 1031 | "application/json", 1032 | func() { 1033 | Function("newFunc") 1034 | }, 1035 | ) 1036 | Consumes( 1037 | "application/json", 1038 | func() { 1039 | Package("encoding/json") 1040 | }, 1041 | )`, 1042 | }, 1043 | "with multi consumes definition": { 1044 | definition: design.APIDefinition{ 1045 | Consumes: []*design.EncodingDefinition{ 1046 | &design.EncodingDefinition{ 1047 | MIMETypes: []string{"application/json"}, 1048 | Encoder: false, 1049 | }, 1050 | &design.EncodingDefinition{ 1051 | MIMETypes: []string{ 1052 | "application/json", 1053 | "application/xml", 1054 | }, 1055 | Encoder: false, 1056 | }, 1057 | }, 1058 | }, 1059 | expected: `Consumes("application/json") 1060 | Consumes( 1061 | "application/json", 1062 | "application/xml", 1063 | )`, 1064 | }, 1065 | "with single consumes definition": { 1066 | definition: design.APIDefinition{ 1067 | Consumes: []*design.EncodingDefinition{ 1068 | &design.EncodingDefinition{ 1069 | MIMETypes: []string{"application/json"}, 1070 | Encoder: false, 1071 | }, 1072 | }, 1073 | }, 1074 | expected: `Consumes("application/json")`, 1075 | }, 1076 | "with function definition": { 1077 | definition: design.APIDefinition{ 1078 | Consumes: []*design.EncodingDefinition{ 1079 | &design.EncodingDefinition{ 1080 | MIMETypes: []string{"application/json"}, 1081 | Function: "newFunc", 1082 | Encoder: false, 1083 | }, 1084 | }, 1085 | }, 1086 | expected: `Consumes( 1087 | "application/json", 1088 | func() { 1089 | Function("newFunc") 1090 | }, 1091 | )`, 1092 | }, 1093 | "with package path definition": { 1094 | definition: design.APIDefinition{ 1095 | Consumes: []*design.EncodingDefinition{ 1096 | &design.EncodingDefinition{ 1097 | MIMETypes: []string{"application/json"}, 1098 | PackagePath: "encoding/json", 1099 | Encoder: false, 1100 | }, 1101 | }, 1102 | }, 1103 | expected: `Consumes( 1104 | "application/json", 1105 | func() { 1106 | Package("encoding/json") 1107 | }, 1108 | )`, 1109 | }, 1110 | "without definition": { 1111 | definition: design.APIDefinition{}, 1112 | expected: ``, 1113 | }, 1114 | } 1115 | for k, tc := range cases { 1116 | buf := new(bytes.Buffer) 1117 | if err := tmpl.ExecuteTemplate(buf, "consumes", tc.definition); err != nil { 1118 | t.Fatalf("Execute returned %s", err) 1119 | } 1120 | actual := buf.String() 1121 | if actual != tc.expected { 1122 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1123 | } 1124 | } 1125 | } 1126 | 1127 | func TestContactTmpl(t *testing.T) { 1128 | cases := map[string]struct { 1129 | definition interface{} 1130 | expected string 1131 | }{ 1132 | "with name/email/url definition": { 1133 | definition: design.APIDefinition{ 1134 | Contact: &design.ContactDefinition{ 1135 | Name: "Name of contact", 1136 | Email: "foo@bar.com", 1137 | URL: "http://localhost/contact", 1138 | }, 1139 | }, 1140 | expected: `Contact(func() { 1141 | Name("Name of contact") 1142 | Email("foo@bar.com") 1143 | URL("http://localhost/contact") 1144 | })`, 1145 | }, 1146 | "with name definition": { 1147 | definition: design.APIDefinition{ 1148 | Contact: &design.ContactDefinition{ 1149 | Name: "Name of contact", 1150 | }, 1151 | }, 1152 | expected: `Contact(func() { 1153 | Name("Name of contact") 1154 | })`, 1155 | }, 1156 | "with email definition": { 1157 | definition: design.APIDefinition{ 1158 | Contact: &design.ContactDefinition{ 1159 | Email: "foo@bar.com", 1160 | }, 1161 | }, 1162 | expected: `Contact(func() { 1163 | Email("foo@bar.com") 1164 | })`, 1165 | }, 1166 | "with url definition": { 1167 | definition: design.APIDefinition{ 1168 | Contact: &design.ContactDefinition{ 1169 | URL: "http://localhost/contact", 1170 | }, 1171 | }, 1172 | expected: `Contact(func() { 1173 | URL("http://localhost/contact") 1174 | })`, 1175 | }, 1176 | "without empty contact definition": { 1177 | definition: design.APIDefinition{ 1178 | Contact: &design.ContactDefinition{}, 1179 | }, 1180 | expected: `Contact(func() { 1181 | })`, 1182 | }, 1183 | "without definition": { 1184 | definition: design.APIDefinition{}, 1185 | expected: ``, 1186 | }, 1187 | } 1188 | for k, tc := range cases { 1189 | buf := new(bytes.Buffer) 1190 | if err := tmpl.ExecuteTemplate(buf, "contact", tc.definition); err != nil { 1191 | t.Fatalf("Execute returned %s", err) 1192 | } 1193 | actual := buf.String() 1194 | if actual != tc.expected { 1195 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1196 | } 1197 | } 1198 | } 1199 | 1200 | func TestDELETETmpl(t *testing.T) { 1201 | cases := map[string]struct { 1202 | definition interface{} 1203 | expected string 1204 | }{ 1205 | "with delete definition": { 1206 | definition: design.RouteDefinition{ 1207 | Verb: "DELETE", 1208 | Path: "/", 1209 | }, 1210 | expected: `DELETE("/")`, 1211 | }, 1212 | "with get definition": { 1213 | definition: design.RouteDefinition{ 1214 | Verb: "GET", 1215 | Path: "/", 1216 | }, 1217 | expected: ``, 1218 | }, 1219 | "without definition": { 1220 | definition: design.RouteDefinition{}, 1221 | expected: ``, 1222 | }, 1223 | } 1224 | for k, tc := range cases { 1225 | buf := new(bytes.Buffer) 1226 | if err := tmpl.ExecuteTemplate(buf, "delete", tc.definition); err != nil { 1227 | t.Fatalf("Execute returned %s", err) 1228 | } 1229 | actual := buf.String() 1230 | if actual != tc.expected { 1231 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1232 | } 1233 | } 1234 | } 1235 | 1236 | func TestDocsTmpl(t *testing.T) { 1237 | cases := map[string]struct { 1238 | definition interface{} 1239 | expected string 1240 | }{ 1241 | "with api definition": { 1242 | definition: design.APIDefinition{ 1243 | Docs: &design.DocsDefinition{ 1244 | Description: "Description of API", 1245 | URL: "http://localhost/api", 1246 | }, 1247 | }, 1248 | expected: `Docs(func() { 1249 | Description("Description of API") 1250 | URL("http://localhost/api") 1251 | })`, 1252 | }, 1253 | "with action definition": { 1254 | definition: design.ActionDefinition{ 1255 | Docs: &design.DocsDefinition{ 1256 | Description: "Description of action", 1257 | URL: "http://localhost/action", 1258 | }, 1259 | }, 1260 | expected: `Docs(func() { 1261 | Description("Description of action") 1262 | URL("http://localhost/action") 1263 | })`, 1264 | }, 1265 | "with name definition": { 1266 | definition: design.APIDefinition{ 1267 | Docs: &design.DocsDefinition{ 1268 | Description: "Description of action", 1269 | }, 1270 | }, 1271 | expected: `Docs(func() { 1272 | Description("Description of action") 1273 | })`, 1274 | }, 1275 | "with url definition": { 1276 | definition: design.APIDefinition{ 1277 | Docs: &design.DocsDefinition{ 1278 | URL: "http://localhost/api", 1279 | }, 1280 | }, 1281 | expected: `Docs(func() { 1282 | URL("http://localhost/api") 1283 | })`, 1284 | }, 1285 | "without empty docs definition": { 1286 | definition: design.APIDefinition{ 1287 | Docs: &design.DocsDefinition{}, 1288 | }, 1289 | expected: `Docs(func() { 1290 | })`, 1291 | }, 1292 | "without definition": { 1293 | definition: design.APIDefinition{}, 1294 | expected: ``, 1295 | }, 1296 | } 1297 | for k, tc := range cases { 1298 | buf := new(bytes.Buffer) 1299 | if err := tmpl.ExecuteTemplate(buf, "docs", tc.definition); err != nil { 1300 | t.Fatalf("Execute returned %s", err) 1301 | } 1302 | actual := buf.String() 1303 | if actual != tc.expected { 1304 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1305 | } 1306 | } 1307 | } 1308 | 1309 | func TestGETTmpl(t *testing.T) { 1310 | cases := map[string]struct { 1311 | definition interface{} 1312 | expected string 1313 | }{ 1314 | "with get definition": { 1315 | definition: design.RouteDefinition{ 1316 | Verb: "GET", 1317 | Path: "/", 1318 | }, 1319 | expected: `GET("/")`, 1320 | }, 1321 | "with post definition": { 1322 | definition: design.RouteDefinition{ 1323 | Verb: "POST", 1324 | Path: "/", 1325 | }, 1326 | expected: ``, 1327 | }, 1328 | "without definition": { 1329 | definition: design.RouteDefinition{}, 1330 | expected: ``, 1331 | }, 1332 | } 1333 | for k, tc := range cases { 1334 | buf := new(bytes.Buffer) 1335 | if err := tmpl.ExecuteTemplate(buf, "get", tc.definition); err != nil { 1336 | t.Fatalf("Execute returned %s", err) 1337 | } 1338 | actual := buf.String() 1339 | if actual != tc.expected { 1340 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1341 | } 1342 | } 1343 | } 1344 | 1345 | func TestHEADTmpl(t *testing.T) { 1346 | cases := map[string]struct { 1347 | definition interface{} 1348 | expected string 1349 | }{ 1350 | "with head definition": { 1351 | definition: design.RouteDefinition{ 1352 | Verb: "HEAD", 1353 | Path: "/", 1354 | }, 1355 | expected: `HEAD("/")`, 1356 | }, 1357 | "with get definition": { 1358 | definition: design.RouteDefinition{ 1359 | Verb: "GET", 1360 | Path: "/", 1361 | }, 1362 | expected: ``, 1363 | }, 1364 | "without definition": { 1365 | definition: design.RouteDefinition{}, 1366 | expected: ``, 1367 | }, 1368 | } 1369 | for k, tc := range cases { 1370 | buf := new(bytes.Buffer) 1371 | if err := tmpl.ExecuteTemplate(buf, "head", tc.definition); err != nil { 1372 | t.Fatalf("Execute returned %s", err) 1373 | } 1374 | actual := buf.String() 1375 | if actual != tc.expected { 1376 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1377 | } 1378 | } 1379 | } 1380 | 1381 | func TestLicenseTmpl(t *testing.T) { 1382 | cases := map[string]struct { 1383 | definition interface{} 1384 | expected string 1385 | }{ 1386 | "with name/url definition": { 1387 | definition: design.APIDefinition{ 1388 | License: &design.LicenseDefinition{ 1389 | Name: "Name of license", 1390 | URL: "http://localhost/license", 1391 | }, 1392 | }, 1393 | expected: `License(func() { 1394 | Name("Name of license") 1395 | URL("http://localhost/license") 1396 | })`, 1397 | }, 1398 | "with name definition": { 1399 | definition: design.APIDefinition{ 1400 | License: &design.LicenseDefinition{ 1401 | Name: "Name of license", 1402 | }, 1403 | }, 1404 | expected: `License(func() { 1405 | Name("Name of license") 1406 | })`, 1407 | }, 1408 | "with url definition": { 1409 | definition: design.APIDefinition{ 1410 | License: &design.LicenseDefinition{ 1411 | URL: "http://localhost/license", 1412 | }, 1413 | }, 1414 | expected: `License(func() { 1415 | URL("http://localhost/license") 1416 | })`, 1417 | }, 1418 | "without empty license definition": { 1419 | definition: design.APIDefinition{ 1420 | License: &design.LicenseDefinition{}, 1421 | }, 1422 | expected: `License(func() { 1423 | })`, 1424 | }, 1425 | "without definition": { 1426 | definition: design.APIDefinition{}, 1427 | expected: ``, 1428 | }, 1429 | } 1430 | for k, tc := range cases { 1431 | buf := new(bytes.Buffer) 1432 | if err := tmpl.ExecuteTemplate(buf, "license", tc.definition); err != nil { 1433 | t.Fatalf("Execute returned %s", err) 1434 | } 1435 | actual := buf.String() 1436 | if actual != tc.expected { 1437 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1438 | } 1439 | } 1440 | } 1441 | 1442 | func TestOPTIONSTmpl(t *testing.T) { 1443 | cases := map[string]struct { 1444 | definition interface{} 1445 | expected string 1446 | }{ 1447 | "with options definition": { 1448 | definition: design.RouteDefinition{ 1449 | Verb: "OPTIONS", 1450 | Path: "/", 1451 | }, 1452 | expected: `OPTIONS("/")`, 1453 | }, 1454 | "with get definition": { 1455 | definition: design.RouteDefinition{ 1456 | Verb: "GET", 1457 | Path: "/", 1458 | }, 1459 | expected: ``, 1460 | }, 1461 | "without definition": { 1462 | definition: design.RouteDefinition{}, 1463 | expected: ``, 1464 | }, 1465 | } 1466 | for k, tc := range cases { 1467 | buf := new(bytes.Buffer) 1468 | if err := tmpl.ExecuteTemplate(buf, "options", tc.definition); err != nil { 1469 | t.Fatalf("Execute returned %s", err) 1470 | } 1471 | actual := buf.String() 1472 | if actual != tc.expected { 1473 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1474 | } 1475 | } 1476 | } 1477 | 1478 | func TestOriginTmpl(t *testing.T) { 1479 | cases := map[string]struct { 1480 | definition interface{} 1481 | expected string 1482 | }{ 1483 | "with multi definition": { 1484 | definition: design.APIDefinition{ 1485 | Origins: map[string]*design.CORSDefinition{ 1486 | "1": &design.CORSDefinition{}, 1487 | "2": &design.CORSDefinition{}, 1488 | }, 1489 | }, 1490 | expected: `Origin("", func() { 1491 | }) 1492 | Origin("", func() { 1493 | })`, 1494 | }, 1495 | "with single definition": { 1496 | definition: design.APIDefinition{ 1497 | Origins: map[string]*design.CORSDefinition{ 1498 | "*": &design.CORSDefinition{ 1499 | Origin: "*", 1500 | Headers: []string{ 1501 | "Authorization", 1502 | "Link", 1503 | }, 1504 | Methods: []string{ 1505 | "GET", 1506 | }, 1507 | Exposed: []string{ 1508 | "Authorization", 1509 | }, 1510 | MaxAge: 600, 1511 | Credentials: true, 1512 | Regexp: false, 1513 | }, 1514 | }, 1515 | }, 1516 | expected: `Origin("*", func() { 1517 | Headers( 1518 | "Authorization", 1519 | "Link", 1520 | ) 1521 | Methods("GET") 1522 | Expose("Authorization") 1523 | MaxAge(600) 1524 | Credentials() 1525 | })`, 1526 | }, 1527 | "without definition": { 1528 | definition: design.APIDefinition{}, 1529 | expected: ``, 1530 | }, 1531 | } 1532 | for k, tc := range cases { 1533 | buf := new(bytes.Buffer) 1534 | if err := tmpl.ExecuteTemplate(buf, "origin", tc.definition); err != nil { 1535 | t.Fatalf("Execute returned %s", err) 1536 | } 1537 | actual := buf.String() 1538 | if actual != tc.expected { 1539 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1540 | } 1541 | } 1542 | } 1543 | 1544 | func TestPATCHTmpl(t *testing.T) { 1545 | cases := map[string]struct { 1546 | definition interface{} 1547 | expected string 1548 | }{ 1549 | "with patch definition": { 1550 | definition: design.RouteDefinition{ 1551 | Verb: "PATCH", 1552 | Path: "/", 1553 | }, 1554 | expected: `PATCH("/")`, 1555 | }, 1556 | "with get definition": { 1557 | definition: design.RouteDefinition{ 1558 | Verb: "GET", 1559 | Path: "/", 1560 | }, 1561 | expected: ``, 1562 | }, 1563 | "without definition": { 1564 | definition: design.RouteDefinition{}, 1565 | expected: ``, 1566 | }, 1567 | } 1568 | for k, tc := range cases { 1569 | buf := new(bytes.Buffer) 1570 | if err := tmpl.ExecuteTemplate(buf, "patch", tc.definition); err != nil { 1571 | t.Fatalf("Execute returned %s", err) 1572 | } 1573 | actual := buf.String() 1574 | if actual != tc.expected { 1575 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1576 | } 1577 | } 1578 | } 1579 | 1580 | func TestPayloadTmpl(t *testing.T) { 1581 | cases := map[string]struct { 1582 | definition interface{} 1583 | expected string 1584 | }{ 1585 | "with definition": { 1586 | definition: design.ActionDefinition{ 1587 | Payload: &design.UserTypeDefinition{ 1588 | TypeName: "FooPayload", 1589 | }, 1590 | }, 1591 | expected: `Payload(FooPayload)`, 1592 | }, 1593 | "without definition": { 1594 | definition: design.ActionDefinition{}, 1595 | expected: ``, 1596 | }, 1597 | } 1598 | for k, tc := range cases { 1599 | buf := new(bytes.Buffer) 1600 | if err := tmpl.ExecuteTemplate(buf, "payload", tc.definition); err != nil { 1601 | t.Fatalf("Execute returned %s", err) 1602 | } 1603 | actual := buf.String() 1604 | if actual != tc.expected { 1605 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1606 | } 1607 | } 1608 | } 1609 | 1610 | func TestPOSTTmpl(t *testing.T) { 1611 | cases := map[string]struct { 1612 | definition interface{} 1613 | expected string 1614 | }{ 1615 | "with post definition": { 1616 | definition: design.RouteDefinition{ 1617 | Verb: "POST", 1618 | Path: "/", 1619 | }, 1620 | expected: `POST("/")`, 1621 | }, 1622 | "with get definition": { 1623 | definition: design.RouteDefinition{ 1624 | Verb: "GET", 1625 | Path: "/", 1626 | }, 1627 | expected: ``, 1628 | }, 1629 | "without definition": { 1630 | definition: design.RouteDefinition{}, 1631 | expected: ``, 1632 | }, 1633 | } 1634 | for k, tc := range cases { 1635 | buf := new(bytes.Buffer) 1636 | if err := tmpl.ExecuteTemplate(buf, "post", tc.definition); err != nil { 1637 | t.Fatalf("Execute returned %s", err) 1638 | } 1639 | actual := buf.String() 1640 | if actual != tc.expected { 1641 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1642 | } 1643 | } 1644 | } 1645 | 1646 | func TestPUTTmpl(t *testing.T) { 1647 | cases := map[string]struct { 1648 | definition interface{} 1649 | expected string 1650 | }{ 1651 | "with put definition": { 1652 | definition: design.RouteDefinition{ 1653 | Verb: "PUT", 1654 | Path: "/", 1655 | }, 1656 | expected: `PUT("/")`, 1657 | }, 1658 | "with get definition": { 1659 | definition: design.RouteDefinition{ 1660 | Verb: "GET", 1661 | Path: "/", 1662 | }, 1663 | expected: ``, 1664 | }, 1665 | "without definition": { 1666 | definition: design.RouteDefinition{}, 1667 | expected: ``, 1668 | }, 1669 | } 1670 | for k, tc := range cases { 1671 | buf := new(bytes.Buffer) 1672 | if err := tmpl.ExecuteTemplate(buf, "put", tc.definition); err != nil { 1673 | t.Fatalf("Execute returned %s", err) 1674 | } 1675 | actual := buf.String() 1676 | if actual != tc.expected { 1677 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1678 | } 1679 | } 1680 | } 1681 | 1682 | func TestProducesTmpl(t *testing.T) { 1683 | cases := map[string]struct { 1684 | definition interface{} 1685 | expected string 1686 | }{ 1687 | "with complex definition": { 1688 | definition: design.APIDefinition{ 1689 | Produces: []*design.EncodingDefinition{ 1690 | &design.EncodingDefinition{ 1691 | MIMETypes: []string{"application/json"}, 1692 | Encoder: true, 1693 | }, 1694 | &design.EncodingDefinition{ 1695 | MIMETypes: []string{ 1696 | "application/json", 1697 | "application/xml", 1698 | }, 1699 | Encoder: true, 1700 | }, 1701 | &design.EncodingDefinition{ 1702 | MIMETypes: []string{"application/json"}, 1703 | Function: "newFunc", 1704 | Encoder: true, 1705 | }, 1706 | &design.EncodingDefinition{ 1707 | MIMETypes: []string{"application/json"}, 1708 | PackagePath: "encoding/json", 1709 | Encoder: true, 1710 | }, 1711 | }, 1712 | }, 1713 | expected: `Produces("application/json") 1714 | Produces( 1715 | "application/json", 1716 | "application/xml", 1717 | ) 1718 | Produces( 1719 | "application/json", 1720 | func() { 1721 | Function("newFunc") 1722 | }, 1723 | ) 1724 | Produces( 1725 | "application/json", 1726 | func() { 1727 | Package("encoding/json") 1728 | }, 1729 | )`, 1730 | }, 1731 | "with multi produces definition": { 1732 | definition: design.APIDefinition{ 1733 | Produces: []*design.EncodingDefinition{ 1734 | &design.EncodingDefinition{ 1735 | MIMETypes: []string{"application/json"}, 1736 | Encoder: true, 1737 | }, 1738 | &design.EncodingDefinition{ 1739 | MIMETypes: []string{ 1740 | "application/json", 1741 | "application/xml", 1742 | }, 1743 | Encoder: true, 1744 | }, 1745 | }, 1746 | }, 1747 | expected: `Produces("application/json") 1748 | Produces( 1749 | "application/json", 1750 | "application/xml", 1751 | )`, 1752 | }, 1753 | "with single produces definition": { 1754 | definition: design.APIDefinition{ 1755 | Produces: []*design.EncodingDefinition{ 1756 | &design.EncodingDefinition{ 1757 | MIMETypes: []string{"application/json"}, 1758 | Encoder: true, 1759 | }, 1760 | }, 1761 | }, 1762 | expected: `Produces("application/json")`, 1763 | }, 1764 | "with function definition": { 1765 | definition: design.APIDefinition{ 1766 | Produces: []*design.EncodingDefinition{ 1767 | &design.EncodingDefinition{ 1768 | MIMETypes: []string{"application/json"}, 1769 | Function: "newFunc", 1770 | Encoder: true, 1771 | }, 1772 | }, 1773 | }, 1774 | expected: `Produces( 1775 | "application/json", 1776 | func() { 1777 | Function("newFunc") 1778 | }, 1779 | )`, 1780 | }, 1781 | "with package path definition": { 1782 | definition: design.APIDefinition{ 1783 | Produces: []*design.EncodingDefinition{ 1784 | &design.EncodingDefinition{ 1785 | MIMETypes: []string{"application/json"}, 1786 | PackagePath: "encoding/json", 1787 | Encoder: true, 1788 | }, 1789 | }, 1790 | }, 1791 | expected: `Produces( 1792 | "application/json", 1793 | func() { 1794 | Package("encoding/json") 1795 | }, 1796 | )`, 1797 | }, 1798 | "without definition": { 1799 | definition: design.APIDefinition{}, 1800 | expected: ``, 1801 | }, 1802 | } 1803 | for k, tc := range cases { 1804 | buf := new(bytes.Buffer) 1805 | if err := tmpl.ExecuteTemplate(buf, "produces", tc.definition); err != nil { 1806 | t.Fatalf("Execute returned %s", err) 1807 | } 1808 | actual := buf.String() 1809 | if actual != tc.expected { 1810 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1811 | } 1812 | } 1813 | } 1814 | 1815 | func TestResponseTmpl(t *testing.T) { 1816 | cases := map[string]struct { 1817 | definition interface{} 1818 | expected string 1819 | }{ 1820 | "with multi definition": { 1821 | definition: design.ActionDefinition{ 1822 | Responses: map[string]*design.ResponseDefinition{ 1823 | "FooMedia": &design.ResponseDefinition{ 1824 | Name: "FooMedia", 1825 | }, 1826 | "BarMedia": &design.ResponseDefinition{ 1827 | Name: "BarMedia", 1828 | }, 1829 | }, 1830 | }, 1831 | expected: `Response(BarMedia) 1832 | Response(FooMedia)`, 1833 | }, 1834 | "with single definition": { 1835 | definition: design.ActionDefinition{ 1836 | Responses: map[string]*design.ResponseDefinition{ 1837 | "FooMediat": &design.ResponseDefinition{ 1838 | Name: "FooMedia", 1839 | }, 1840 | }, 1841 | }, 1842 | expected: `Response(FooMedia)`, 1843 | }, 1844 | "without definition": { 1845 | definition: design.ActionDefinition{}, 1846 | expected: ``, 1847 | }, 1848 | } 1849 | for k, tc := range cases { 1850 | buf := new(bytes.Buffer) 1851 | if err := tmpl.ExecuteTemplate(buf, "response", tc.definition); err != nil { 1852 | t.Fatalf("Execute returned %s", err) 1853 | } 1854 | actual := buf.String() 1855 | if actual != tc.expected { 1856 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1857 | } 1858 | } 1859 | } 1860 | 1861 | func TestRoutingTmpl(t *testing.T) { 1862 | cases := map[string]struct { 1863 | definition interface{} 1864 | expected string 1865 | }{ 1866 | "with multi definition": { 1867 | definition: design.ActionDefinition{ 1868 | Routes: []*design.RouteDefinition{ 1869 | &design.RouteDefinition{ 1870 | Verb: "CONNECT", 1871 | Path: "/", 1872 | }, 1873 | &design.RouteDefinition{ 1874 | Verb: "DELETE", 1875 | Path: "/", 1876 | }, 1877 | &design.RouteDefinition{ 1878 | Verb: "GET", 1879 | Path: "/", 1880 | }, 1881 | &design.RouteDefinition{ 1882 | Verb: "HEAD", 1883 | Path: "/", 1884 | }, 1885 | &design.RouteDefinition{ 1886 | Verb: "OPTIONS", 1887 | Path: "/", 1888 | }, 1889 | &design.RouteDefinition{ 1890 | Verb: "PATCH", 1891 | Path: "/", 1892 | }, 1893 | &design.RouteDefinition{ 1894 | Verb: "POST", 1895 | Path: "/", 1896 | }, 1897 | &design.RouteDefinition{ 1898 | Verb: "PUT", 1899 | Path: "/", 1900 | }, 1901 | &design.RouteDefinition{ 1902 | Verb: "TRACE", 1903 | Path: "/", 1904 | }, 1905 | }, 1906 | }, 1907 | expected: `Routing( 1908 | CONNECT("/"), 1909 | DELETE("/"), 1910 | GET("/"), 1911 | HEAD("/"), 1912 | OPTIONS("/"), 1913 | PATCH("/"), 1914 | POST("/"), 1915 | PUT("/"), 1916 | TRACE("/"), 1917 | )`, 1918 | }, 1919 | "with single definition": { 1920 | definition: design.ActionDefinition{ 1921 | Routes: []*design.RouteDefinition{ 1922 | &design.RouteDefinition{ 1923 | Verb: "GET", 1924 | Path: "/", 1925 | }, 1926 | }, 1927 | }, 1928 | expected: `Routing(GET("/"))`, 1929 | }, 1930 | "without definition": { 1931 | definition: design.ActionDefinition{}, 1932 | expected: ``, 1933 | }, 1934 | } 1935 | for k, tc := range cases { 1936 | buf := new(bytes.Buffer) 1937 | if err := tmpl.ExecuteTemplate(buf, "routing", tc.definition); err != nil { 1938 | t.Fatalf("Execute returned %s", err) 1939 | } 1940 | actual := buf.String() 1941 | if actual != tc.expected { 1942 | t.Errorf("%s: got %v, expected %v", k, actual, tc.expected) 1943 | } 1944 | } 1945 | } 1946 | 1947 | func TestTRACETmpl(t *testing.T) { 1948 | cases := map[string]struct { 1949 | definition interface{} 1950 | expected string 1951 | }{ 1952 | "with trace definition": { 1953 | definition: design.RouteDefinition{ 1954 | Verb: "TRACE", 1955 | Path: "/", 1956 | }, 1957 | expected: `TRACE("/")`, 1958 | }, 1959 | "with get definition": { 1960 | definition: design.RouteDefinition{ 1961 | Verb: "GET", 1962 | Path: "/", 1963 | }, 1964 | expected: ``, 1965 | }, 1966 | "without definition": { 1967 | definition: design.RouteDefinition{}, 1968 | expected: ``, 1969 | }, 1970 | } 1971 | for k, tc := range cases { 1972 | buf := new(bytes.Buffer) 1973 | if err := tmpl.ExecuteTemplate(buf, "trace", tc.definition); err != nil { 1974 | t.Fatalf("Execute returned %s", err) 1975 | } 1976 | actual := buf.String() 1977 | if actual != tc.expected { 1978 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 1979 | } 1980 | } 1981 | } 1982 | 1983 | func TestTypeTmpl(t *testing.T) { 1984 | cases := map[string]struct { 1985 | definition interface{} 1986 | expected string 1987 | }{ 1988 | "with multi definition": { 1989 | definition: design.APIDefinition{ 1990 | Types: map[string]*design.UserTypeDefinition{ 1991 | "FooPayload": &design.UserTypeDefinition{ 1992 | TypeName: "FooPayload", 1993 | }, 1994 | "BarPayload": &design.UserTypeDefinition{ 1995 | TypeName: "BarPayload", 1996 | }, 1997 | }, 1998 | }, 1999 | expected: `var BarPayload = Type("BarPayload", func() { 2000 | }) 2001 | var FooPayload = Type("FooPayload", func() { 2002 | })`, 2003 | }, 2004 | "with single definition": { 2005 | definition: design.APIDefinition{ 2006 | Types: map[string]*design.UserTypeDefinition{ 2007 | "FooPayload": &design.UserTypeDefinition{ 2008 | TypeName: "FooPayload", 2009 | }, 2010 | }, 2011 | }, 2012 | expected: `var FooPayload = Type("FooPayload", func() { 2013 | })`, 2014 | }, 2015 | "without definition": { 2016 | definition: design.APIDefinition{}, 2017 | expected: ``, 2018 | }, 2019 | } 2020 | for k, tc := range cases { 2021 | buf := new(bytes.Buffer) 2022 | if err := tmpl.ExecuteTemplate(buf, "type", tc.definition); err != nil { 2023 | t.Fatalf("Execute returned %s", err) 2024 | } 2025 | actual := buf.String() 2026 | if actual != tc.expected { 2027 | t.Errorf("%s: \ngot:\n%v\nexpected:\n%v", k, actual, tc.expected) 2028 | } 2029 | } 2030 | } 2031 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/tchssk/ago/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | --------------------------------------------------------------------------------