github.com/goldeneggg/goa@v1.3.1/goagen/gen_client/generator.go (about)

     1  package genclient
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"path/filepath"
     9  	"sort"
    10  	"strings"
    11  	"text/template"
    12  
    13  	"github.com/goadesign/goa/design"
    14  	"github.com/goadesign/goa/dslengine"
    15  	"github.com/goadesign/goa/goagen/codegen"
    16  	"github.com/goadesign/goa/goagen/gen_app"
    17  	"github.com/goadesign/goa/goagen/utils"
    18  )
    19  
    20  // Filename used to generate all data types (without the ".go" extension)
    21  const typesFileName = "datatypes"
    22  
    23  //NewGenerator returns an initialized instance of a Go Client Generator
    24  func NewGenerator(options ...Option) *Generator {
    25  	g := &Generator{}
    26  
    27  	for _, option := range options {
    28  		option(g)
    29  	}
    30  
    31  	return g
    32  }
    33  
    34  // Generator is the application code generator.
    35  type Generator struct {
    36  	API            *design.APIDefinition // The API definition
    37  	OutDir         string                // Path to output directory
    38  	Target         string                // Name of generated package
    39  	ToolDirName    string                // Name of tool directory where CLI main is generated once
    40  	Tool           string                // Name of CLI tool
    41  	NoTool         bool                  // Whether to skip tool generation
    42  	genfiles       []string
    43  	encoders       []*genapp.EncoderTemplateData
    44  	decoders       []*genapp.EncoderTemplateData
    45  	encoderImports []string
    46  }
    47  
    48  // Generate is the generator entry point called by the meta generator.
    49  func Generate() (files []string, err error) {
    50  	var (
    51  		outDir, target, toolDir, tool, ver string
    52  		notool, regen                      bool
    53  	)
    54  	dtool := defaultToolName(design.Design)
    55  
    56  	set := flag.NewFlagSet("client", flag.PanicOnError)
    57  	set.StringVar(&outDir, "out", "", "")
    58  	set.StringVar(&target, "pkg", "client", "")
    59  	set.StringVar(&toolDir, "tooldir", "tool", "")
    60  	set.StringVar(&tool, "tool", dtool, "")
    61  	set.StringVar(&ver, "version", "", "")
    62  	set.BoolVar(&notool, "notool", false, "")
    63  	set.BoolVar(&regen, "regen", false, "")
    64  	set.String("design", "", "")
    65  	set.Bool("force", false, "")
    66  	set.Bool("notest", false, "")
    67  	set.Parse(os.Args[1:])
    68  
    69  	// First check compatibility
    70  	if err := codegen.CheckVersion(ver); err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	// Now proceed
    75  	target = codegen.Goify(target, false)
    76  	g := &Generator{OutDir: outDir, Target: target, ToolDirName: toolDir, Tool: tool, NoTool: notool, API: design.Design}
    77  
    78  	return g.Generate()
    79  }
    80  
    81  // Generate generats the client package and CLI.
    82  func (g *Generator) Generate() (_ []string, err error) {
    83  	if g.API == nil {
    84  		return nil, fmt.Errorf("missing API definition, make sure design is properly initialized")
    85  	}
    86  
    87  	go utils.Catch(nil, func() { g.Cleanup() })
    88  
    89  	defer func() {
    90  		if err != nil {
    91  			g.Cleanup()
    92  		}
    93  	}()
    94  
    95  	firstNonEmpty := func(args ...string) string {
    96  		for _, value := range args {
    97  			if len(value) > 0 {
    98  				return value
    99  			}
   100  		}
   101  		return ""
   102  	}
   103  
   104  	g.Target = firstNonEmpty(g.Target, "client")
   105  	g.ToolDirName = firstNonEmpty(g.ToolDirName, "tool")
   106  	g.Tool = firstNonEmpty(g.Tool, defaultToolName(g.API))
   107  
   108  	codegen.Reserved[g.Target] = true
   109  
   110  	// Setup output directories as needed
   111  	var pkgDir, toolDir, cliDir string
   112  	{
   113  		if !g.NoTool {
   114  			toolDir = filepath.Join(g.OutDir, g.ToolDirName, g.Tool)
   115  			if _, err = os.Stat(toolDir); err != nil {
   116  				if err = os.MkdirAll(toolDir, 0755); err != nil {
   117  					return
   118  				}
   119  			}
   120  
   121  			cliDir = filepath.Join(g.OutDir, g.ToolDirName, "cli")
   122  			if err = os.RemoveAll(cliDir); err != nil {
   123  				return
   124  			}
   125  			if err = os.MkdirAll(cliDir, 0755); err != nil {
   126  				return
   127  			}
   128  		}
   129  
   130  		pkgDir = filepath.Join(g.OutDir, g.Target)
   131  		if err = os.RemoveAll(pkgDir); err != nil {
   132  			return
   133  		}
   134  		if err = os.MkdirAll(pkgDir, 0755); err != nil {
   135  			return
   136  		}
   137  	}
   138  
   139  	// Setup generation
   140  	var funcs template.FuncMap
   141  	var clientPkg string
   142  	{
   143  		funcs = template.FuncMap{
   144  			"add":                func(a, b int) int { return a + b },
   145  			"cmdFieldType":       cmdFieldType,
   146  			"defaultPath":        defaultPath,
   147  			"escapeBackticks":    escapeBackticks,
   148  			"goify":              codegen.Goify,
   149  			"gotypedef":          codegen.GoTypeDef,
   150  			"gotypedesc":         codegen.GoTypeDesc,
   151  			"gotypename":         codegen.GoTypeName,
   152  			"gotyperef":          codegen.GoTypeRef,
   153  			"gotyperefext":       goTypeRefExt,
   154  			"join":               join,
   155  			"joinStrings":        strings.Join,
   156  			"multiComment":       multiComment,
   157  			"pathParams":         pathParams,
   158  			"pathTemplate":       pathTemplate,
   159  			"signerType":         signerType,
   160  			"tempvar":            codegen.Tempvar,
   161  			"title":              strings.Title,
   162  			"toString":           toString,
   163  			"typeName":           typeName,
   164  			"format":             format,
   165  			"handleSpecialTypes": handleSpecialTypes,
   166  		}
   167  		clientPkg, err = codegen.PackagePath(pkgDir)
   168  		if err != nil {
   169  			return
   170  		}
   171  		arrayToStringTmpl = template.Must(template.New("client").Funcs(funcs).Parse(arrayToStringT))
   172  	}
   173  
   174  	if !g.NoTool {
   175  		var cliPkg string
   176  		cliPkg, err = codegen.PackagePath(cliDir)
   177  		if err != nil {
   178  			return
   179  		}
   180  
   181  		// Generate tool/main.go (only once)
   182  		mainFile := filepath.Join(toolDir, "main.go")
   183  		if _, err := os.Stat(mainFile); err != nil {
   184  			g.genfiles = append(g.genfiles, toolDir)
   185  			if err = g.generateMain(mainFile, clientPkg, cliPkg, funcs); err != nil {
   186  				return nil, err
   187  			}
   188  		}
   189  
   190  		// Generate tool/cli/commands.go
   191  		g.genfiles = append(g.genfiles, cliDir)
   192  		if err = g.generateCommands(filepath.Join(cliDir, "commands.go"), clientPkg, funcs); err != nil {
   193  			return
   194  		}
   195  	}
   196  
   197  	// Generate client/client.go
   198  	g.genfiles = append(g.genfiles, pkgDir)
   199  	if err = g.generateClient(filepath.Join(pkgDir, "client.go"), clientPkg, funcs); err != nil {
   200  		return
   201  	}
   202  
   203  	// Generate client/$res.go and types.go
   204  	if err = g.generateClientResources(pkgDir, clientPkg, funcs); err != nil {
   205  		return
   206  	}
   207  
   208  	return g.genfiles, nil
   209  }
   210  
   211  func defaultToolName(api *design.APIDefinition) string {
   212  	if api == nil {
   213  		return ""
   214  	}
   215  	return strings.Replace(strings.ToLower(api.Name), " ", "-", -1) + "-cli"
   216  }
   217  
   218  // Cleanup removes all the files generated by this generator during the last invokation of Generate.
   219  func (g *Generator) Cleanup() {
   220  	for _, f := range g.genfiles {
   221  		os.Remove(f)
   222  	}
   223  	g.genfiles = nil
   224  }
   225  
   226  func (g *Generator) generateClient(clientFile string, clientPkg string, funcs template.FuncMap) (err error) {
   227  	var file *codegen.SourceFile
   228  	{
   229  		file, err = codegen.SourceFileFor(clientFile)
   230  		if err != nil {
   231  			return
   232  		}
   233  	}
   234  	defer func() {
   235  		file.Close()
   236  		if err == nil {
   237  			err = file.FormatCode()
   238  		}
   239  	}()
   240  	clientTmpl := template.Must(template.New("client").Funcs(funcs).Parse(clientTmpl))
   241  
   242  	// Compute list of encoders and decoders
   243  	encoders, err := genapp.BuildEncoders(g.API.Produces, true)
   244  	if err != nil {
   245  		return err
   246  	}
   247  	decoders, err := genapp.BuildEncoders(g.API.Consumes, false)
   248  	if err != nil {
   249  		return err
   250  	}
   251  	im := make(map[string]bool)
   252  	for _, data := range encoders {
   253  		im[data.PackagePath] = true
   254  	}
   255  	for _, data := range decoders {
   256  		im[data.PackagePath] = true
   257  	}
   258  	var packagePaths []string
   259  	for packagePath := range im {
   260  		if packagePath != "github.com/goadesign/goa" {
   261  			packagePaths = append(packagePaths, packagePath)
   262  		}
   263  	}
   264  	sort.Strings(packagePaths)
   265  
   266  	// Setup codegen
   267  	imports := []*codegen.ImportSpec{
   268  		codegen.SimpleImport("net/http"),
   269  		codegen.SimpleImport("github.com/goadesign/goa"),
   270  		codegen.NewImport("goaclient", "github.com/goadesign/goa/client"),
   271  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   272  	}
   273  	for _, packagePath := range packagePaths {
   274  		imports = append(imports, codegen.SimpleImport(packagePath))
   275  	}
   276  	title := fmt.Sprintf("%s: Client", g.API.Context())
   277  	if err = file.WriteHeader(title, g.Target, imports); err != nil {
   278  		return err
   279  	}
   280  	g.genfiles = append(g.genfiles, clientFile)
   281  
   282  	// Generate
   283  	data := struct {
   284  		API      *design.APIDefinition
   285  		Encoders []*genapp.EncoderTemplateData
   286  		Decoders []*genapp.EncoderTemplateData
   287  	}{
   288  		API:      g.API,
   289  		Encoders: encoders,
   290  		Decoders: decoders,
   291  	}
   292  	err = clientTmpl.Execute(file, data)
   293  	return
   294  }
   295  
   296  func (g *Generator) generateClientResources(pkgDir, clientPkg string, funcs template.FuncMap) error {
   297  	err := g.API.IterateResources(func(res *design.ResourceDefinition) error {
   298  		return g.generateResourceClient(pkgDir, res, funcs)
   299  	})
   300  	if err != nil {
   301  		return err
   302  	}
   303  	if err := g.generateUserTypes(pkgDir); err != nil {
   304  		return err
   305  	}
   306  
   307  	return g.generateMediaTypes(pkgDir, funcs)
   308  }
   309  
   310  func (g *Generator) generateResourceClient(pkgDir string, res *design.ResourceDefinition, funcs template.FuncMap) (err error) {
   311  	payloadTmpl := template.Must(template.New("payload").Funcs(funcs).Parse(payloadTmpl))
   312  	pathTmpl := template.Must(template.New("pathTemplate").Funcs(funcs).Parse(pathTmpl))
   313  
   314  	resFilename := codegen.SnakeCase(res.Name)
   315  	if resFilename == typesFileName {
   316  		// Avoid clash with datatypes.go
   317  		resFilename += "_client"
   318  	}
   319  	filename := filepath.Join(pkgDir, resFilename+".go")
   320  
   321  	var file *codegen.SourceFile
   322  	file, err = codegen.SourceFileFor(filename)
   323  	if err != nil {
   324  		return err
   325  	}
   326  	defer func() {
   327  		file.Close()
   328  		if err == nil {
   329  			err = file.FormatCode()
   330  		}
   331  	}()
   332  	imports := []*codegen.ImportSpec{
   333  		codegen.SimpleImport("bytes"),
   334  		codegen.SimpleImport("encoding/json"),
   335  		codegen.SimpleImport("fmt"),
   336  		codegen.SimpleImport("io"),
   337  		codegen.SimpleImport("io/ioutil"),
   338  		codegen.SimpleImport("net/http"),
   339  		codegen.SimpleImport("net/url"),
   340  		codegen.SimpleImport("os"),
   341  		codegen.SimpleImport("path"),
   342  		codegen.SimpleImport("strconv"),
   343  		codegen.SimpleImport("strings"),
   344  		codegen.SimpleImport("time"),
   345  		codegen.SimpleImport("context"),
   346  		codegen.SimpleImport("golang.org/x/net/websocket"),
   347  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   348  	}
   349  	title := fmt.Sprintf("%s: %s Resource Client", g.API.Context(), res.Name)
   350  	if err = file.WriteHeader(title, g.Target, imports); err != nil {
   351  		return err
   352  	}
   353  	g.genfiles = append(g.genfiles, filename)
   354  
   355  	err = res.IterateFileServers(func(fs *design.FileServerDefinition) error {
   356  		return g.generateFileServer(file, fs, funcs)
   357  	})
   358  	if err != nil {
   359  		return err
   360  	}
   361  
   362  	err = res.IterateActions(func(action *design.ActionDefinition) error {
   363  		if action.Payload != nil {
   364  			found := false
   365  			typeName := action.Payload.TypeName
   366  			for _, t := range design.Design.Types {
   367  				if t.TypeName == typeName {
   368  					found = true
   369  					break
   370  				}
   371  			}
   372  			if !found {
   373  				if err := payloadTmpl.Execute(file, action); err != nil {
   374  					return err
   375  				}
   376  			}
   377  		}
   378  		for i, r := range action.Routes {
   379  			routeParams := r.Params()
   380  			var pd []*paramData
   381  
   382  			for _, p := range routeParams {
   383  				requiredParams, _ := initParams(&design.AttributeDefinition{
   384  					Type: &design.Object{
   385  						p: action.Params.Type.ToObject()[p],
   386  					},
   387  					Validation: &dslengine.ValidationDefinition{
   388  						Required: routeParams,
   389  					},
   390  				})
   391  				pd = append(pd, requiredParams...)
   392  			}
   393  
   394  			data := struct {
   395  				Route  *design.RouteDefinition
   396  				Index  int
   397  				Params []*paramData
   398  			}{
   399  				Route:  r,
   400  				Index:  i,
   401  				Params: pd,
   402  			}
   403  			if err := pathTmpl.Execute(file, data); err != nil {
   404  				return err
   405  			}
   406  		}
   407  		return g.generateActionClient(action, file, funcs)
   408  	})
   409  	return
   410  }
   411  
   412  func (g *Generator) generateFileServer(file *codegen.SourceFile, fs *design.FileServerDefinition, funcs template.FuncMap) error {
   413  	var (
   414  		dir string
   415  
   416  		fsTmpl = template.Must(template.New("fileserver").Funcs(funcs).Parse(fsTmpl))
   417  		name   = g.fileServerMethod(fs)
   418  		wcs    = design.ExtractWildcards(fs.RequestPath)
   419  		scheme = "http"
   420  	)
   421  
   422  	if len(wcs) > 0 {
   423  		dir = "/"
   424  		fileElems := filepath.SplitList(fs.FilePath)
   425  		if len(fileElems) > 1 {
   426  			dir = fileElems[len(fileElems)-2]
   427  		}
   428  	}
   429  	if len(design.Design.Schemes) > 0 {
   430  		scheme = design.Design.Schemes[0]
   431  	}
   432  	requestDir, _ := path.Split(fs.RequestPath)
   433  
   434  	data := struct {
   435  		Name            string // Download functionn name
   436  		RequestPath     string // File server request path
   437  		FilePath        string // File server file path
   438  		FileName        string // Filename being download if request path has no wildcard
   439  		DirName         string // Parent directory name if request path has wildcard
   440  		RequestDir      string // Request path without wildcard suffix
   441  		CanonicalScheme string // HTTP scheme
   442  	}{
   443  		Name:            name,
   444  		RequestPath:     fs.RequestPath,
   445  		FilePath:        fs.FilePath,
   446  		FileName:        filepath.Base(fs.FilePath),
   447  		DirName:         dir,
   448  		RequestDir:      requestDir,
   449  		CanonicalScheme: scheme,
   450  	}
   451  	return fsTmpl.Execute(file, data)
   452  }
   453  
   454  func (g *Generator) generateActionClient(action *design.ActionDefinition, file *codegen.SourceFile, funcs template.FuncMap) error {
   455  	var (
   456  		params        []string
   457  		names         []string
   458  		queryParams   []*paramData
   459  		headers       []*paramData
   460  		signer        string
   461  		clientsTmpl   = template.Must(template.New("clients").Funcs(funcs).Parse(clientsTmpl))
   462  		requestsTmpl  = template.Must(template.New("requests").Funcs(funcs).Parse(requestsTmpl))
   463  		clientsWSTmpl = template.Must(template.New("clientsws").Funcs(funcs).Parse(clientsWSTmpl))
   464  	)
   465  	if action.Payload != nil {
   466  		params = append(params, "payload "+codegen.GoTypeRef(action.Payload, action.Payload.AllRequired(), 1, false))
   467  		names = append(names, "payload")
   468  	}
   469  
   470  	initParamsScoped := func(att *design.AttributeDefinition) []*paramData {
   471  		reqData, optData := initParams(att)
   472  
   473  		sort.Sort(byParamName(reqData))
   474  		sort.Sort(byParamName(optData))
   475  
   476  		// Update closure
   477  		for _, p := range reqData {
   478  			names = append(names, p.VarName)
   479  			params = append(params, p.VarName+" "+cmdFieldType(p.Attribute.Type, false))
   480  		}
   481  		for _, p := range optData {
   482  			names = append(names, p.VarName)
   483  			params = append(params, p.VarName+" "+cmdFieldType(p.Attribute.Type, p.Attribute.Type.IsPrimitive()))
   484  		}
   485  		return append(reqData, optData...)
   486  	}
   487  	queryParams = initParamsScoped(action.QueryParams)
   488  	headers = initParamsScoped(action.Headers)
   489  
   490  	if action.Security != nil {
   491  		signer = codegen.Goify(action.Security.Scheme.SchemeName, true)
   492  	}
   493  	data := struct {
   494  		Name               string
   495  		ResourceName       string
   496  		Description        string
   497  		Routes             []*design.RouteDefinition
   498  		HasPayload         bool
   499  		HasMultiContent    bool
   500  		DefaultContentType string
   501  		Params             string
   502  		ParamNames         string
   503  		CanonicalScheme    string
   504  		Signer             string
   505  		QueryParams        []*paramData
   506  		Headers            []*paramData
   507  	}{
   508  		Name:               action.Name,
   509  		ResourceName:       action.Parent.Name,
   510  		Description:        action.Description,
   511  		Routes:             action.Routes,
   512  		HasPayload:         action.Payload != nil,
   513  		HasMultiContent:    len(design.Design.Consumes) > 1,
   514  		DefaultContentType: design.Design.Consumes[0].MIMETypes[0],
   515  		Params:             strings.Join(params, ", "),
   516  		ParamNames:         strings.Join(names, ", "),
   517  		CanonicalScheme:    action.CanonicalScheme(),
   518  		Signer:             signer,
   519  		QueryParams:        queryParams,
   520  		Headers:            headers,
   521  	}
   522  	if action.WebSocket() {
   523  		return clientsWSTmpl.Execute(file, data)
   524  	}
   525  	if err := clientsTmpl.Execute(file, data); err != nil {
   526  		return err
   527  	}
   528  	return requestsTmpl.Execute(file, data)
   529  }
   530  
   531  // fileServerMethod returns the name of the client method for downloading assets served by the given
   532  // file server.
   533  // Note: the implementation opts for generating good names rather than names that are guaranteed to
   534  // be unique. This means that the generated code could be potentially incorrect in the rare cases
   535  // where it produces the same names for two different file servers. This should be addressed later
   536  // (when it comes up?) using metadata to let users override the default.
   537  func (g *Generator) fileServerMethod(fs *design.FileServerDefinition) string {
   538  	var (
   539  		suffix string
   540  
   541  		wcs      = design.ExtractWildcards(fs.RequestPath)
   542  		reqElems = strings.Split(fs.RequestPath, "/")
   543  	)
   544  
   545  	if len(wcs) == 0 {
   546  		suffix = path.Base(fs.RequestPath)
   547  		ext := filepath.Ext(suffix)
   548  		suffix = strings.TrimSuffix(suffix, ext)
   549  		suffix += codegen.Goify(ext, true)
   550  	} else {
   551  		if len(reqElems) == 1 {
   552  			suffix = filepath.Base(fs.RequestPath)
   553  			suffix = suffix[1:] // remove "*" prefix
   554  		} else {
   555  			suffix = reqElems[len(reqElems)-2] // should work most of the time
   556  		}
   557  	}
   558  	return "Download" + codegen.Goify(suffix, true)
   559  }
   560  
   561  // generateMediaTypes iterates through the media types and generate the data structures and
   562  // marshaling code.
   563  func (g *Generator) generateMediaTypes(pkgDir string, funcs template.FuncMap) (err error) {
   564  	funcs["decodegotyperef"] = decodeGoTypeRef
   565  	funcs["decodegotypename"] = decodeGoTypeName
   566  	typeDecodeTmpl := template.Must(template.New("typeDecode").Funcs(funcs).Parse(typeDecodeTmpl))
   567  	var (
   568  		mtFile string
   569  		mtWr   *genapp.MediaTypesWriter
   570  	)
   571  	{
   572  		mtFile = filepath.Join(pkgDir, "media_types.go")
   573  		mtWr, err = genapp.NewMediaTypesWriter(mtFile)
   574  		if err != nil {
   575  			return
   576  		}
   577  	}
   578  	defer func() {
   579  		mtWr.Close()
   580  		if err == nil {
   581  			err = mtWr.FormatCode()
   582  		}
   583  	}()
   584  	title := fmt.Sprintf("%s: Application Media Types", g.API.Context())
   585  	imports := []*codegen.ImportSpec{
   586  		codegen.SimpleImport("github.com/goadesign/goa"),
   587  		codegen.SimpleImport("fmt"),
   588  		codegen.SimpleImport("net/http"),
   589  		codegen.SimpleImport("time"),
   590  		codegen.SimpleImport("unicode/utf8"),
   591  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   592  	}
   593  	for _, v := range g.API.MediaTypes {
   594  		imports = codegen.AttributeImports(v.AttributeDefinition, imports, nil)
   595  	}
   596  	if err = mtWr.WriteHeader(title, g.Target, imports); err != nil {
   597  		return err
   598  	}
   599  	g.genfiles = append(g.genfiles, mtFile)
   600  	err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error {
   601  		if (mt.Type.IsObject() || mt.Type.IsArray()) && !mt.IsError() {
   602  			if err := mtWr.Execute(mt); err != nil {
   603  				return err
   604  			}
   605  		}
   606  		err := mt.IterateViews(func(view *design.ViewDefinition) error {
   607  			p, _, err := mt.Project(view.Name)
   608  			if err != nil {
   609  				return err
   610  			}
   611  			return typeDecodeTmpl.Execute(mtWr.SourceFile, p)
   612  		})
   613  		return err
   614  	})
   615  	return
   616  }
   617  
   618  // generateUserTypes iterates through the user types and generates the data structures and
   619  // marshaling code.
   620  func (g *Generator) generateUserTypes(pkgDir string) (err error) {
   621  	var (
   622  		utFile string
   623  		utWr   *genapp.UserTypesWriter
   624  	)
   625  	{
   626  		utFile = filepath.Join(pkgDir, "user_types.go")
   627  		utWr, err = genapp.NewUserTypesWriter(utFile)
   628  		if err != nil {
   629  			return
   630  		}
   631  	}
   632  	defer func() {
   633  		utWr.Close()
   634  		if err == nil {
   635  			err = utWr.FormatCode()
   636  		}
   637  	}()
   638  	title := fmt.Sprintf("%s: Application User Types", g.API.Context())
   639  	imports := []*codegen.ImportSpec{
   640  		codegen.SimpleImport("github.com/goadesign/goa"),
   641  		codegen.SimpleImport("fmt"),
   642  		codegen.SimpleImport("time"),
   643  		codegen.SimpleImport("unicode/utf8"),
   644  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   645  	}
   646  	for _, v := range g.API.Types {
   647  		imports = codegen.AttributeImports(v.AttributeDefinition, imports, nil)
   648  	}
   649  	if err = utWr.WriteHeader(title, g.Target, imports); err != nil {
   650  		return err
   651  	}
   652  	g.genfiles = append(g.genfiles, utFile)
   653  	err = g.API.IterateUserTypes(func(t *design.UserTypeDefinition) error {
   654  		return utWr.Execute(t)
   655  	})
   656  	return
   657  }
   658  
   659  // join is a code generation helper function that generates a function signature built from
   660  // concatenating the properties (name type) of the given attribute type (assuming it's an object).
   661  // join accepts an optional slice of strings which indicates the order in which the parameters
   662  // should appear in the signature. If pos is specified then it must list all the parameters. If
   663  // it's not specified then parameters are sorted alphabetically.
   664  func join(att *design.AttributeDefinition, usePointers bool, pos ...[]string) string {
   665  	if att == nil {
   666  		return ""
   667  	}
   668  	obj := att.Type.ToObject()
   669  	elems := make([]string, len(obj))
   670  	var keys []string
   671  	if len(pos) > 0 {
   672  		keys = pos[0]
   673  		if len(keys) != len(obj) {
   674  			panic("invalid position slice, lenght does not match attribute field count") // bug
   675  		}
   676  	} else {
   677  		keys = make([]string, len(obj))
   678  		i := 0
   679  		for n := range obj {
   680  			keys[i] = n
   681  			i++
   682  		}
   683  		sort.Strings(keys)
   684  	}
   685  	for i, n := range keys {
   686  		a := obj[n]
   687  		elems[i] = fmt.Sprintf("%s %s", codegen.Goify(n, false),
   688  			cmdFieldType(a.Type, usePointers && !a.IsRequired(n)))
   689  	}
   690  	return strings.Join(elems, ", ")
   691  }
   692  
   693  // escapeBackticks is a code generation helper that escapes backticks in a string.
   694  func escapeBackticks(text string) string {
   695  	return strings.Replace(text, "`", "`+\"`\"+`", -1)
   696  }
   697  
   698  // multiComment produces a Go comment containing the given string taking into account newlines.
   699  func multiComment(text string) string {
   700  	lines := strings.Split(text, "\n")
   701  	nl := make([]string, len(lines))
   702  	for i, l := range lines {
   703  		nl[i] = "// " + strings.TrimSpace(l)
   704  	}
   705  	return strings.Join(nl, "\n")
   706  }
   707  
   708  // gotTypeRefExt computes the type reference for a type in a different package.
   709  func goTypeRefExt(t design.DataType, tabs int, pkg string) string {
   710  	ref := codegen.GoTypeRef(t, nil, tabs, false)
   711  	if strings.HasPrefix(ref, "*") {
   712  		return fmt.Sprintf("%s.%s", pkg, ref[1:])
   713  	}
   714  	return fmt.Sprintf("%s.%s", pkg, ref)
   715  }
   716  
   717  // decodeGoTypeRef handles the case where the type being decoded is a error response media type.
   718  func decodeGoTypeRef(t design.DataType, required []string, tabs int, private bool) string {
   719  	mt, ok := t.(*design.MediaTypeDefinition)
   720  	if ok && mt.IsError() {
   721  		return "*goa.ErrorResponse"
   722  	}
   723  	return codegen.GoTypeRef(t, required, tabs, private)
   724  }
   725  
   726  // decodeGoTypeName handles the case where the type being decoded is a error response media type.
   727  func decodeGoTypeName(t design.DataType, required []string, tabs int, private bool) string {
   728  	mt, ok := t.(*design.MediaTypeDefinition)
   729  	if ok && mt.IsError() {
   730  		return "goa.ErrorResponse"
   731  	}
   732  	return codegen.GoTypeName(t, required, tabs, private)
   733  }
   734  
   735  // cmdFieldType computes the Go type name used to store command flags of the given design type.
   736  func cmdFieldType(t design.DataType, point bool) string {
   737  	var pointer, suffix string
   738  	if point && !t.IsArray() {
   739  		pointer = "*"
   740  	}
   741  	suffix = codegen.GoNativeType(t)
   742  	return pointer + suffix
   743  }
   744  
   745  // cmdFieldTypeString computes the Go type name used to store command flags of the given design type. Complex types are String
   746  func cmdFieldTypeString(t design.DataType, point bool) string {
   747  	var pointer, suffix string
   748  	if point && !t.IsArray() {
   749  		pointer = "*"
   750  	}
   751  	if t.Kind() == design.UUIDKind || t.Kind() == design.DateTimeKind || t.Kind() == design.AnyKind || t.Kind() == design.NumberKind || t.Kind() == design.BooleanKind {
   752  		suffix = "string"
   753  	} else if isArrayOfType(t, design.UUIDKind, design.DateTimeKind, design.AnyKind, design.NumberKind, design.BooleanKind) {
   754  		suffix = "[]string"
   755  	} else {
   756  		suffix = codegen.GoNativeType(t)
   757  	}
   758  	return pointer + suffix
   759  }
   760  
   761  func isArrayOfType(array design.DataType, kinds ...design.Kind) bool {
   762  	if !array.IsArray() {
   763  		return false
   764  	}
   765  	kind := array.ToArray().ElemType.Type.Kind()
   766  	for _, t := range kinds {
   767  		if t == kind {
   768  			return true
   769  		}
   770  	}
   771  	return false
   772  }
   773  
   774  // template used to produce code that serializes arrays of simple values into comma separated
   775  // strings.
   776  var arrayToStringTmpl *template.Template
   777  
   778  // toString generates Go code that converts the given simple type attribute into a string.
   779  func toString(name, target string, att *design.AttributeDefinition) string {
   780  	switch actual := att.Type.(type) {
   781  	case design.Primitive:
   782  		switch actual.Kind() {
   783  		case design.IntegerKind:
   784  			return fmt.Sprintf("%s := strconv.Itoa(%s)", target, name)
   785  		case design.BooleanKind:
   786  			return fmt.Sprintf("%s := strconv.FormatBool(%s)", target, name)
   787  		case design.NumberKind:
   788  			return fmt.Sprintf("%s := strconv.FormatFloat(%s, 'f', -1, 64)", target, name)
   789  		case design.StringKind:
   790  			return fmt.Sprintf("%s := %s", target, name)
   791  		case design.DateTimeKind:
   792  			return fmt.Sprintf("%s := %s.Format(time.RFC3339)", target, strings.Replace(name, "*", "", -1)) // remove pointer if present
   793  		case design.UUIDKind:
   794  			return fmt.Sprintf("%s := %s.String()", target, strings.Replace(name, "*", "", -1)) // remove pointer if present
   795  		case design.AnyKind:
   796  			return fmt.Sprintf("%s := fmt.Sprintf(\"%%v\", %s)", target, name)
   797  		default:
   798  			panic("unknown primitive type")
   799  		}
   800  	case *design.Array:
   801  		data := map[string]interface{}{
   802  			"Name":     name,
   803  			"Target":   target,
   804  			"ElemType": actual.ElemType,
   805  		}
   806  		return codegen.RunTemplate(arrayToStringTmpl, data)
   807  	default:
   808  		panic("cannot convert non simple type " + att.Type.Name() + " to string") // bug
   809  	}
   810  }
   811  
   812  // defaultPath returns the first route path for the given action that does not take any wildcard,
   813  // empty string if none.
   814  func defaultPath(action *design.ActionDefinition) string {
   815  	for _, r := range action.Routes {
   816  		candidate := r.FullPath()
   817  		if !strings.ContainsRune(candidate, ':') {
   818  			return candidate
   819  		}
   820  	}
   821  	return ""
   822  }
   823  
   824  // signerType returns the name of the client signer used for the defined security model on the Action
   825  func signerType(scheme *design.SecuritySchemeDefinition) string {
   826  	switch scheme.Kind {
   827  	case design.JWTSecurityKind:
   828  		return "goaclient.JWTSigner" // goa client package imported under goaclient
   829  	case design.OAuth2SecurityKind:
   830  		return "goaclient.OAuth2Signer"
   831  	case design.APIKeySecurityKind:
   832  		return "goaclient.APIKeySigner"
   833  	case design.BasicAuthSecurityKind:
   834  		return "goaclient.BasicSigner"
   835  	}
   836  	return ""
   837  }
   838  
   839  // pathTemplate returns a fmt format suitable to build a request path to the route.
   840  func pathTemplate(r *design.RouteDefinition) string {
   841  	return design.WildcardRegex.ReplaceAllLiteralString(r.FullPath(), "/%s")
   842  }
   843  
   844  // pathParams return the function signature of the path factory function for the given route.
   845  func pathParams(r *design.RouteDefinition) string {
   846  	pnames := r.Params()
   847  	params := make(design.Object, len(pnames))
   848  	for _, p := range pnames {
   849  		params[p] = r.Parent.Params.Type.ToObject()[p]
   850  	}
   851  	return join(&design.AttributeDefinition{Type: params}, false, pnames)
   852  }
   853  
   854  // typeName returns Go type name of given MediaType definition.
   855  func typeName(mt *design.MediaTypeDefinition) string {
   856  	if mt.IsError() {
   857  		return "ErrorResponse"
   858  	}
   859  	return codegen.GoTypeName(mt, mt.AllRequired(), 1, false)
   860  }
   861  
   862  // initParams returns required and optional paramData extracted from given attribute definition.
   863  func initParams(att *design.AttributeDefinition) ([]*paramData, []*paramData) {
   864  	if att == nil {
   865  		return nil, nil
   866  	}
   867  	obj := att.Type.ToObject()
   868  	var reqParamData []*paramData
   869  	var optParamData []*paramData
   870  	for n, q := range obj {
   871  		varName := codegen.Goify(n, false)
   872  		param := &paramData{
   873  			Name:      n,
   874  			VarName:   varName,
   875  			Attribute: q,
   876  		}
   877  		if q.Type.IsPrimitive() {
   878  			param.MustToString = q.Type.Kind() != design.StringKind
   879  			if att.IsRequired(n) {
   880  				param.ValueName = varName
   881  				reqParamData = append(reqParamData, param)
   882  			} else {
   883  				param.ValueName = "*" + varName
   884  				param.CheckNil = true
   885  				optParamData = append(optParamData, param)
   886  			}
   887  		} else {
   888  			if q.Type.IsArray() {
   889  				param.IsArray = true
   890  				param.ElemAttribute = q.Type.ToArray().ElemType
   891  			}
   892  			param.MustToString = true
   893  			param.ValueName = varName
   894  			param.CheckNil = true
   895  			if att.IsRequired(n) {
   896  				reqParamData = append(reqParamData, param)
   897  			} else {
   898  				optParamData = append(optParamData, param)
   899  			}
   900  		}
   901  	}
   902  
   903  	return reqParamData, optParamData
   904  }
   905  
   906  // paramData is the data structure holding the information needed to generate query params and
   907  // headers handling code.
   908  type paramData struct {
   909  	Name          string
   910  	VarName       string
   911  	ValueName     string
   912  	Attribute     *design.AttributeDefinition
   913  	ElemAttribute *design.AttributeDefinition
   914  	MustToString  bool
   915  	IsArray       bool
   916  	CheckNil      bool
   917  }
   918  
   919  type byParamName []*paramData
   920  
   921  func (b byParamName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
   922  func (b byParamName) Less(i, j int) bool { return b[i].Name < b[j].Name }
   923  func (b byParamName) Len() int           { return len(b) }
   924  
   925  const (
   926  	arrayToStringT = `	{{ $tmp := tempvar }}{{ $tmp }} := make([]string, len({{ .Name }}))
   927  	for i, e := range {{ .Name }} {
   928  		{{ $tmp2 := tempvar }}{{ toString "e" $tmp2 .ElemType }}
   929  		{{ $tmp }}[i] = {{ $tmp2 }}
   930  	}
   931  	{{ .Target }} := strings.Join({{ $tmp }}, ",")`
   932  
   933  	payloadTmpl = `// {{ gotypename .Payload nil 0 false }} is the {{ .Parent.Name }} {{ .Name }} action payload.
   934  type {{ gotypename .Payload nil 1 false }} {{ gotypedef .Payload 0 true false }}
   935  `
   936  
   937  	typeDecodeTmpl = `{{ $typeName := typeName . }}{{ $funcName := printf "Decode%s" $typeName }}// {{ $funcName }} decodes the {{ $typeName }} instance encoded in resp body.
   938  func (c *Client) {{ $funcName }}(resp *http.Response) ({{ decodegotyperef . .AllRequired 0 false }}, error) {
   939  	var decoded {{ decodegotypename . .AllRequired 0 false }}
   940  	err := c.Decoder.Decode(&decoded, resp.Body, resp.Header.Get("Content-Type"))
   941  	return {{ if .IsObject }}&{{ end }}decoded, err
   942  }
   943  `
   944  
   945  	pathTmpl = `{{ $funcName := printf "%sPath%s" (goify (printf "%s%s" .Route.Parent.Name (title .Route.Parent.Parent.Name)) true) ((or (and .Index (add .Index 1)) "") | printf "%v") }}{{/*
   946  */}}// {{ $funcName }} computes a request path to the {{ .Route.Parent.Name }} action of {{ .Route.Parent.Parent.Name }}.
   947  func {{ $funcName }}({{ pathParams .Route }}) string {
   948  	{{ range $i, $param := .Params }}{{/*
   949  */}}{{ toString $param.VarName (printf "param%d" $i) $param.Attribute }}
   950  	{{ end }}
   951  	return fmt.Sprintf({{ printf "%q" (pathTemplate .Route) }}{{ range $i, $param := .Params }}, {{ printf "param%d" $i }}{{ end }})
   952  }
   953  `
   954  
   955  	clientsTmpl = `{{ $funcName := goify (printf "%s%s" .Name (title .ResourceName)) true }}{{ $desc := .Description }}{{/*
   956  */}}{{ if $desc }}{{ multiComment $desc }}{{ else }}{{/*
   957  */}}// {{ $funcName }} makes a request to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource{{ end }}
   958  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}{{ if and .HasPayload .HasMultiContent }}, contentType string{{ end }}) (*http.Response, error) {
   959  	req, err := c.New{{ $funcName }}Request(ctx, path{{ if .ParamNames }}, {{ .ParamNames }}{{ end }}{{ if and .HasPayload .HasMultiContent }}, contentType{{ end }})
   960  	if err != nil {
   961  		return nil, err
   962  	}
   963  	return c.Client.Do(ctx, req)
   964  }
   965  `
   966  
   967  	clientsWSTmpl = `{{ $funcName := goify (printf "%s%s" .Name (title .ResourceName)) true }}{{ $desc := .Description }}{{/*
   968  */}}{{ if $desc }}{{ multiComment $desc }}{{ else }}// {{ $funcName }} establishes a websocket connection to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource{{ end }}
   969  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}) (*websocket.Conn, error) {
   970  	scheme := c.Scheme
   971  	if scheme == "" {
   972  		scheme = "{{ .CanonicalScheme }}"
   973  	}
   974  	u := url.URL{Host: c.Host, Scheme: scheme, Path: path}
   975  {{ if .QueryParams }}	values := u.Query()
   976  {{ range .QueryParams }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
   977  	{{ end }}{{/*
   978  
   979  // ARRAY
   980  */}}{{ if .IsArray }}		for _, p := range {{ .VarName }} {
   981  {{ if .MustToString }}{{ $tmp := tempvar }}			{{ toString "p" $tmp .ElemAttribute }}
   982  			values.Add("{{ .Name }}", {{ $tmp }})
   983  {{ else }}			values.Add("{{ .Name }}", {{ .ValueName }})
   984  {{ end }}}{{/*
   985  
   986  // NON STRING
   987  */}}{{ else if .MustToString }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
   988  	values.Set("{{ .Name }}", {{ $tmp }})
   989  {{/*
   990  
   991  // STRING
   992  */}}{{ else }}	values.Set("{{ .Name }}", {{ .ValueName }})
   993  {{ end }}{{ if .CheckNil }}	}
   994  {{ end }}{{ end }}	u.RawQuery = values.Encode()
   995  {{ end }}	url_ := u.String()
   996  	cfg, err := websocket.NewConfig(url_, url_)
   997  	if err != nil {
   998  		return nil, err
   999  	}
  1000  {{ range $header := .Headers }}{{ $tmp := tempvar }}	{{ toString $header.VarName $tmp $header.Attribute }}
  1001  	cfg.Header["{{ $header.Name }}"] = []string{ {{ $tmp }} }
  1002  {{ end }}	return websocket.DialConfig(cfg)
  1003  }
  1004  `
  1005  
  1006  	fsTmpl = `// {{ .Name }} downloads {{ if .DirName }}{{ .DirName }}files with the given filename{{ else }}{{ .FileName }}{{ end }} and writes it to the file dest.
  1007  // It returns the number of bytes downloaded in case of success.
  1008  func (c * Client) {{ .Name }}(ctx context.Context, {{ if .DirName }}filename, {{ end }}dest string) (int64, error) {
  1009  	scheme := c.Scheme
  1010  	if scheme == "" {
  1011  		scheme = "{{ .CanonicalScheme }}"
  1012  	}
  1013  {{ if .DirName }}	p := path.Join("{{ .RequestDir }}", filename)
  1014  {{ end }}	u := url.URL{Host: c.Host, Scheme: scheme, Path: {{ if .DirName }}p{{ else }}"{{ .RequestPath }}"{{ end }}}
  1015  	req, err := http.NewRequest("GET", u.String(), nil)
  1016  	if err != nil {
  1017  		return 0, err
  1018  	}
  1019  	resp, err := c.Client.Do(ctx, req)
  1020  	if err != nil {
  1021  		return 0, err
  1022  	}
  1023  	if resp.StatusCode != 200 {
  1024  		var body string
  1025  		if b, err := ioutil.ReadAll(resp.Body); err != nil {
  1026  			if len(b) > 0 {
  1027  				body = ": "+ string(b)
  1028  			}
  1029  		}
  1030  		return 0, fmt.Errorf("%s%s", resp.Status, body)
  1031  	}
  1032  	defer resp.Body.Close()
  1033  	out, err := os.Create(dest)
  1034  	if err != nil {
  1035  		return 0, err
  1036  	}
  1037  	defer out.Close()
  1038  	return io.Copy(out, resp.Body)
  1039  }
  1040  `
  1041  
  1042  	requestsTmpl = `{{ $funcName := goify (printf "New%s%sRequest" (title .Name) (title .ResourceName)) true }}{{/*
  1043  */}}// {{ $funcName }} create the request corresponding to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource.
  1044  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}{{ if .HasPayload }}{{ if .HasMultiContent }}, contentType string{{ end }}{{ end }}) (*http.Request, error) {
  1045  {{ if .HasPayload }}	var body bytes.Buffer
  1046  {{ if .HasMultiContent }}	if contentType == "" {
  1047  		contentType = "*/*" // Use default encoder
  1048  	}
  1049  {{ end }}	err := c.Encoder.Encode(payload, &body, {{ if .HasMultiContent }}contentType{{ else }}"*/*"{{ end }})
  1050  	if err != nil {
  1051  		return nil, fmt.Errorf("failed to encode body: %s", err)
  1052  	}
  1053  {{ end }}	scheme := c.Scheme
  1054  	if scheme == "" {
  1055  		scheme = "{{ .CanonicalScheme }}"
  1056  	}
  1057  	u := url.URL{Host: c.Host, Scheme: scheme, Path: path}
  1058  {{ if .QueryParams }}	values := u.Query()
  1059  {{ range .QueryParams }}{{/*
  1060  
  1061  // ARRAY
  1062  */}}{{ if .IsArray }}		for _, p := range {{ .VarName }} {
  1063  {{ if .MustToString }}{{ $tmp := tempvar }}			{{ toString "p" $tmp .ElemAttribute }}
  1064  			values.Add("{{ .Name }}", {{ $tmp }})
  1065  {{ else }}			values.Add("{{ .Name }}", {{ .ValueName }})
  1066  {{ end }}	 }
  1067  {{/*
  1068  
  1069  // NON STRING
  1070  */}}{{ else if .MustToString }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1071  	{{ end }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
  1072  	values.Set("{{ .Name }}", {{ $tmp }})
  1073  {{ if .CheckNil }}	}
  1074  {{ end }}{{/*
  1075  
  1076  // STRING
  1077  */}}{{ else }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1078  	{{ end }}	values.Set("{{ .Name }}", {{ .ValueName }})
  1079  {{ if .CheckNil }}	}
  1080  {{ end }}{{ end }}{{ end }}	u.RawQuery = values.Encode()
  1081  {{ end }}{{ if .HasPayload }}	req, err := http.NewRequest({{ $route := index .Routes 0 }}"{{ $route.Verb }}", u.String(), &body)
  1082  {{ else }}	req, err := http.NewRequest({{ $route := index .Routes 0 }}"{{ $route.Verb }}", u.String(), nil)
  1083  {{ end }}	if err != nil {
  1084  		return nil, err
  1085  	}
  1086  {{ if or .HasPayload .Headers }}	header := req.Header
  1087  {{ if .HasPayload }}{{ if .HasMultiContent }}	if contentType == "*/*" {
  1088  		header.Set("Content-Type", "{{ .DefaultContentType }}")
  1089  	} else {
  1090  		header.Set("Content-Type", contentType)
  1091  	}
  1092  {{ else }}	header.Set("Content-Type", "{{ .DefaultContentType }}")
  1093  {{ end }}{{ end }}{{ range .Headers }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1094  {{ end }}{{ if .MustToString }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
  1095  	header.Set("{{ .Name }}", {{ $tmp }}){{ else }}
  1096  	header.Set("{{ .Name }}", {{ .ValueName }})
  1097  {{ end }}{{ if .CheckNil }}	}{{ end }}
  1098  {{ end }}{{ end }}{{ if .Signer }}	if c.{{ .Signer }}Signer != nil {
  1099  		if err := c.{{ .Signer }}Signer.Sign(req); err != nil {
  1100  			return nil, err
  1101  		}
  1102  	}
  1103  {{ end }}	return req, nil
  1104  }
  1105  `
  1106  
  1107  	clientTmpl = `// Client is the {{ .API.Name }} service client.
  1108  type Client struct {
  1109  	*goaclient.Client{{range $security := .API.SecuritySchemes }}{{ $signer := signerType $security }}{{ if $signer }}
  1110  	{{ goify $security.SchemeName true }}Signer goaclient.Signer{{ end }}{{ end }}
  1111  	Encoder *goa.HTTPEncoder
  1112  	Decoder *goa.HTTPDecoder
  1113  }
  1114  
  1115  // New instantiates the client.
  1116  func New(c goaclient.Doer) *Client {
  1117  	client := &Client{
  1118  		Client: goaclient.New(c),
  1119  		Encoder: goa.NewHTTPEncoder(),
  1120  		Decoder: goa.NewHTTPDecoder(),
  1121  	}
  1122  
  1123  {{ if .Encoders }}	// Setup encoders and decoders
  1124  {{ range .Encoders }}{{/*
  1125  */}}	client.Encoder.Register({{ .PackageName }}.{{ .Function }}, "{{ joinStrings .MIMETypes "\", \"" }}")
  1126  {{ end }}{{ range .Decoders }}{{/*
  1127  */}}	client.Decoder.Register({{ .PackageName }}.{{ .Function }}, "{{ joinStrings .MIMETypes "\", \"" }}")
  1128  {{ end }}
  1129  
  1130  	// Setup default encoder and decoder
  1131  {{ range .Encoders }}{{ if .Default }}{{/*
  1132  */}}	client.Encoder.Register({{ .PackageName }}.{{ .Function }}, "*/*")
  1133  {{ end }}{{ end }}{{ range .Decoders }}{{ if .Default }}{{/*
  1134  */}}	client.Decoder.Register({{ .PackageName }}.{{ .Function }}, "*/*")
  1135  {{ end }}{{ end }}
  1136  {{ end }}	return client
  1137  }
  1138  
  1139  {{range $security := .API.SecuritySchemes }}{{ $signer := signerType $security }}{{ if $signer }}{{/*
  1140  */}}{{ $name := printf "%sSigner" (goify $security.SchemeName true) }}{{/*
  1141  */}}// Set{{ $name }} sets the request signer for the {{ $security.SchemeName }} security scheme.
  1142  func (c *Client) Set{{ $name }}(signer goaclient.Signer) {
  1143  	c.{{ $name }} = signer
  1144  }
  1145  {{ end }}{{ end }}
  1146  `
  1147  )