github.com/blp1526/goa@v1.4.0/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("mime/multipart"),
   339  		codegen.SimpleImport("net/http"),
   340  		codegen.SimpleImport("net/url"),
   341  		codegen.SimpleImport("os"),
   342  		codegen.SimpleImport("path"),
   343  		codegen.SimpleImport("path/filepath"),
   344  		codegen.SimpleImport("strconv"),
   345  		codegen.SimpleImport("strings"),
   346  		codegen.SimpleImport("time"),
   347  		codegen.SimpleImport("context"),
   348  		codegen.SimpleImport("golang.org/x/net/websocket"),
   349  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   350  	}
   351  	title := fmt.Sprintf("%s: %s Resource Client", g.API.Context(), res.Name)
   352  	if err = file.WriteHeader(title, g.Target, imports); err != nil {
   353  		return err
   354  	}
   355  	g.genfiles = append(g.genfiles, filename)
   356  
   357  	err = res.IterateFileServers(func(fs *design.FileServerDefinition) error {
   358  		return g.generateFileServer(file, fs, funcs)
   359  	})
   360  	if err != nil {
   361  		return err
   362  	}
   363  
   364  	err = res.IterateActions(func(action *design.ActionDefinition) error {
   365  		if action.Payload != nil {
   366  			found := false
   367  			typeName := action.Payload.TypeName
   368  			for _, t := range design.Design.Types {
   369  				if t.TypeName == typeName {
   370  					found = true
   371  					break
   372  				}
   373  			}
   374  			if !found {
   375  				if err := payloadTmpl.Execute(file, action); err != nil {
   376  					return err
   377  				}
   378  			}
   379  		}
   380  		for i, r := range action.Routes {
   381  			routeParams := r.Params()
   382  			var pd []*paramData
   383  
   384  			for _, p := range routeParams {
   385  				requiredParams, _ := initParams(&design.AttributeDefinition{
   386  					Type: &design.Object{
   387  						p: action.Params.Type.ToObject()[p],
   388  					},
   389  					Validation: &dslengine.ValidationDefinition{
   390  						Required: routeParams,
   391  					},
   392  				})
   393  				pd = append(pd, requiredParams...)
   394  			}
   395  
   396  			data := struct {
   397  				Route  *design.RouteDefinition
   398  				Index  int
   399  				Params []*paramData
   400  			}{
   401  				Route:  r,
   402  				Index:  i,
   403  				Params: pd,
   404  			}
   405  			if err := pathTmpl.Execute(file, data); err != nil {
   406  				return err
   407  			}
   408  		}
   409  		return g.generateActionClient(action, file, funcs)
   410  	})
   411  	return
   412  }
   413  
   414  func (g *Generator) generateFileServer(file *codegen.SourceFile, fs *design.FileServerDefinition, funcs template.FuncMap) error {
   415  	var (
   416  		dir string
   417  
   418  		fsTmpl = template.Must(template.New("fileserver").Funcs(funcs).Parse(fsTmpl))
   419  		name   = g.fileServerMethod(fs)
   420  		wcs    = design.ExtractWildcards(fs.RequestPath)
   421  		scheme = "http"
   422  	)
   423  
   424  	if len(wcs) > 0 {
   425  		dir = "/"
   426  		fileElems := filepath.SplitList(fs.FilePath)
   427  		if len(fileElems) > 1 {
   428  			dir = fileElems[len(fileElems)-2]
   429  		}
   430  	}
   431  	if len(design.Design.Schemes) > 0 {
   432  		scheme = design.Design.Schemes[0]
   433  	}
   434  	requestDir, _ := path.Split(fs.RequestPath)
   435  
   436  	data := struct {
   437  		Name            string // Download functionn name
   438  		RequestPath     string // File server request path
   439  		FilePath        string // File server file path
   440  		FileName        string // Filename being download if request path has no wildcard
   441  		DirName         string // Parent directory name if request path has wildcard
   442  		RequestDir      string // Request path without wildcard suffix
   443  		CanonicalScheme string // HTTP scheme
   444  	}{
   445  		Name:            name,
   446  		RequestPath:     fs.RequestPath,
   447  		FilePath:        fs.FilePath,
   448  		FileName:        filepath.Base(fs.FilePath),
   449  		DirName:         dir,
   450  		RequestDir:      requestDir,
   451  		CanonicalScheme: scheme,
   452  	}
   453  	return fsTmpl.Execute(file, data)
   454  }
   455  
   456  func (g *Generator) generateActionClient(action *design.ActionDefinition, file *codegen.SourceFile, funcs template.FuncMap) error {
   457  	var (
   458  		params        []string
   459  		names         []string
   460  		queryParams   []*paramData
   461  		headers       []*paramData
   462  		signer        string
   463  		clientsTmpl   = template.Must(template.New("clients").Funcs(funcs).Parse(clientsTmpl))
   464  		requestsTmpl  = template.Must(template.New("requests").Funcs(funcs).Parse(requestsTmpl))
   465  		clientsWSTmpl = template.Must(template.New("clientsws").Funcs(funcs).Parse(clientsWSTmpl))
   466  	)
   467  	if action.Payload != nil {
   468  		params = append(params, "payload "+codegen.GoTypeRef(action.Payload, action.Payload.AllRequired(), 1, false))
   469  		names = append(names, "payload")
   470  	}
   471  
   472  	initParamsScoped := func(att *design.AttributeDefinition) []*paramData {
   473  		reqData, optData := initParams(att)
   474  
   475  		sort.Sort(byParamName(reqData))
   476  		sort.Sort(byParamName(optData))
   477  
   478  		// Update closure
   479  		for _, p := range reqData {
   480  			names = append(names, p.VarName)
   481  			params = append(params, p.VarName+" "+cmdFieldType(p.Attribute.Type, false))
   482  		}
   483  		for _, p := range optData {
   484  			names = append(names, p.VarName)
   485  			params = append(params, p.VarName+" "+cmdFieldType(p.Attribute.Type, p.Attribute.Type.IsPrimitive()))
   486  		}
   487  		return append(reqData, optData...)
   488  	}
   489  	queryParams = initParamsScoped(action.QueryParams)
   490  	headers = initParamsScoped(action.Headers)
   491  
   492  	if action.Security != nil {
   493  		signer = codegen.Goify(action.Security.Scheme.SchemeName, true)
   494  	}
   495  	data := struct {
   496  		Name               string
   497  		ResourceName       string
   498  		Description        string
   499  		Routes             []*design.RouteDefinition
   500  		Payload            *design.UserTypeDefinition
   501  		PayloadMultipart   bool
   502  		HasPayload         bool
   503  		HasMultiContent    bool
   504  		DefaultContentType string
   505  		Params             string
   506  		ParamNames         string
   507  		CanonicalScheme    string
   508  		Signer             string
   509  		QueryParams        []*paramData
   510  		Headers            []*paramData
   511  	}{
   512  		Name:               action.Name,
   513  		ResourceName:       action.Parent.Name,
   514  		Description:        action.Description,
   515  		Routes:             action.Routes,
   516  		Payload:            action.Payload,
   517  		PayloadMultipart:   action.PayloadMultipart,
   518  		HasPayload:         action.Payload != nil,
   519  		HasMultiContent:    len(design.Design.Consumes) > 1,
   520  		DefaultContentType: design.Design.Consumes[0].MIMETypes[0],
   521  		Params:             strings.Join(params, ", "),
   522  		ParamNames:         strings.Join(names, ", "),
   523  		CanonicalScheme:    action.CanonicalScheme(),
   524  		Signer:             signer,
   525  		QueryParams:        queryParams,
   526  		Headers:            headers,
   527  	}
   528  	if action.WebSocket() {
   529  		return clientsWSTmpl.Execute(file, data)
   530  	}
   531  	if err := clientsTmpl.Execute(file, data); err != nil {
   532  		return err
   533  	}
   534  	return requestsTmpl.Execute(file, data)
   535  }
   536  
   537  // fileServerMethod returns the name of the client method for downloading assets served by the given
   538  // file server.
   539  // Note: the implementation opts for generating good names rather than names that are guaranteed to
   540  // be unique. This means that the generated code could be potentially incorrect in the rare cases
   541  // where it produces the same names for two different file servers. This should be addressed later
   542  // (when it comes up?) using metadata to let users override the default.
   543  func (g *Generator) fileServerMethod(fs *design.FileServerDefinition) string {
   544  	var (
   545  		suffix string
   546  
   547  		wcs      = design.ExtractWildcards(fs.RequestPath)
   548  		reqElems = strings.Split(fs.RequestPath, "/")
   549  	)
   550  
   551  	if len(wcs) == 0 {
   552  		suffix = path.Base(fs.RequestPath)
   553  		ext := filepath.Ext(suffix)
   554  		suffix = strings.TrimSuffix(suffix, ext)
   555  		suffix += codegen.Goify(ext, true)
   556  	} else {
   557  		if len(reqElems) == 1 {
   558  			suffix = filepath.Base(fs.RequestPath)
   559  			suffix = suffix[1:] // remove "*" prefix
   560  		} else {
   561  			suffix = reqElems[len(reqElems)-2] // should work most of the time
   562  		}
   563  	}
   564  	return "Download" + codegen.Goify(suffix, true)
   565  }
   566  
   567  // generateMediaTypes iterates through the media types and generate the data structures and
   568  // marshaling code.
   569  func (g *Generator) generateMediaTypes(pkgDir string, funcs template.FuncMap) (err error) {
   570  	funcs["decodegotyperef"] = decodeGoTypeRef
   571  	funcs["decodegotypename"] = decodeGoTypeName
   572  	typeDecodeTmpl := template.Must(template.New("typeDecode").Funcs(funcs).Parse(typeDecodeTmpl))
   573  	var (
   574  		mtFile string
   575  		mtWr   *genapp.MediaTypesWriter
   576  	)
   577  	{
   578  		mtFile = filepath.Join(pkgDir, "media_types.go")
   579  		mtWr, err = genapp.NewMediaTypesWriter(mtFile)
   580  		if err != nil {
   581  			return
   582  		}
   583  	}
   584  	defer func() {
   585  		mtWr.Close()
   586  		if err == nil {
   587  			err = mtWr.FormatCode()
   588  		}
   589  	}()
   590  	title := fmt.Sprintf("%s: Application Media Types", g.API.Context())
   591  	imports := []*codegen.ImportSpec{
   592  		codegen.SimpleImport("github.com/goadesign/goa"),
   593  		codegen.SimpleImport("fmt"),
   594  		codegen.SimpleImport("net/http"),
   595  		codegen.SimpleImport("time"),
   596  		codegen.SimpleImport("unicode/utf8"),
   597  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   598  	}
   599  	for _, v := range g.API.MediaTypes {
   600  		imports = codegen.AttributeImports(v.AttributeDefinition, imports, nil)
   601  	}
   602  	if err = mtWr.WriteHeader(title, g.Target, imports); err != nil {
   603  		return err
   604  	}
   605  	g.genfiles = append(g.genfiles, mtFile)
   606  	err = g.API.IterateMediaTypes(func(mt *design.MediaTypeDefinition) error {
   607  		if (mt.Type.IsObject() || mt.Type.IsArray()) && !mt.IsError() {
   608  			if err := mtWr.Execute(mt); err != nil {
   609  				return err
   610  			}
   611  		}
   612  		err := mt.IterateViews(func(view *design.ViewDefinition) error {
   613  			p, _, err := mt.Project(view.Name)
   614  			if err != nil {
   615  				return err
   616  			}
   617  			return typeDecodeTmpl.Execute(mtWr.SourceFile, p)
   618  		})
   619  		return err
   620  	})
   621  	return
   622  }
   623  
   624  // generateUserTypes iterates through the user types and generates the data structures and
   625  // marshaling code.
   626  func (g *Generator) generateUserTypes(pkgDir string) (err error) {
   627  	var (
   628  		utFile string
   629  		utWr   *genapp.UserTypesWriter
   630  	)
   631  	{
   632  		utFile = filepath.Join(pkgDir, "user_types.go")
   633  		utWr, err = genapp.NewUserTypesWriter(utFile)
   634  		if err != nil {
   635  			return
   636  		}
   637  	}
   638  	defer func() {
   639  		utWr.Close()
   640  		if err == nil {
   641  			err = utWr.FormatCode()
   642  		}
   643  	}()
   644  	title := fmt.Sprintf("%s: Application User Types", g.API.Context())
   645  	imports := []*codegen.ImportSpec{
   646  		codegen.SimpleImport("github.com/goadesign/goa"),
   647  		codegen.SimpleImport("fmt"),
   648  		codegen.SimpleImport("time"),
   649  		codegen.SimpleImport("unicode/utf8"),
   650  		codegen.NewImport("uuid", "github.com/goadesign/goa/uuid"),
   651  	}
   652  	for _, v := range g.API.Types {
   653  		imports = codegen.AttributeImports(v.AttributeDefinition, imports, nil)
   654  	}
   655  	if err = utWr.WriteHeader(title, g.Target, imports); err != nil {
   656  		return err
   657  	}
   658  	g.genfiles = append(g.genfiles, utFile)
   659  	err = g.API.IterateUserTypes(func(t *design.UserTypeDefinition) error {
   660  		o := t.Type.ToObject()
   661  		for _, att := range o {
   662  			if att.Type.Kind() == design.FileKind {
   663  				att.Type = design.String
   664  			}
   665  		}
   666  		return utWr.Execute(t)
   667  	})
   668  	return
   669  }
   670  
   671  // join is a code generation helper function that generates a function signature built from
   672  // concatenating the properties (name type) of the given attribute type (assuming it's an object).
   673  // join accepts an optional slice of strings which indicates the order in which the parameters
   674  // should appear in the signature. If pos is specified then it must list all the parameters. If
   675  // it's not specified then parameters are sorted alphabetically.
   676  func join(att *design.AttributeDefinition, usePointers bool, pos ...[]string) string {
   677  	if att == nil {
   678  		return ""
   679  	}
   680  	obj := att.Type.ToObject()
   681  	elems := make([]string, len(obj))
   682  	var keys []string
   683  	if len(pos) > 0 {
   684  		keys = pos[0]
   685  		if len(keys) != len(obj) {
   686  			panic("invalid position slice, lenght does not match attribute field count") // bug
   687  		}
   688  	} else {
   689  		keys = make([]string, len(obj))
   690  		i := 0
   691  		for n := range obj {
   692  			keys[i] = n
   693  			i++
   694  		}
   695  		sort.Strings(keys)
   696  	}
   697  	for i, n := range keys {
   698  		a := obj[n]
   699  		elems[i] = fmt.Sprintf("%s %s", codegen.Goify(n, false),
   700  			cmdFieldType(a.Type, usePointers && !a.IsRequired(n)))
   701  	}
   702  	return strings.Join(elems, ", ")
   703  }
   704  
   705  // escapeBackticks is a code generation helper that escapes backticks in a string.
   706  func escapeBackticks(text string) string {
   707  	return strings.Replace(text, "`", "`+\"`\"+`", -1)
   708  }
   709  
   710  // multiComment produces a Go comment containing the given string taking into account newlines.
   711  func multiComment(text string) string {
   712  	lines := strings.Split(text, "\n")
   713  	nl := make([]string, len(lines))
   714  	for i, l := range lines {
   715  		nl[i] = "// " + strings.TrimSpace(l)
   716  	}
   717  	return strings.Join(nl, "\n")
   718  }
   719  
   720  // gotTypeRefExt computes the type reference for a type in a different package.
   721  func goTypeRefExt(t design.DataType, tabs int, pkg string) string {
   722  	ref := codegen.GoTypeRef(t, nil, tabs, false)
   723  	if strings.HasPrefix(ref, "*") {
   724  		return fmt.Sprintf("%s.%s", pkg, ref[1:])
   725  	}
   726  	return fmt.Sprintf("%s.%s", pkg, ref)
   727  }
   728  
   729  // decodeGoTypeRef handles the case where the type being decoded is a error response media type.
   730  func decodeGoTypeRef(t design.DataType, required []string, tabs int, private bool) string {
   731  	mt, ok := t.(*design.MediaTypeDefinition)
   732  	if ok && mt.IsError() {
   733  		return "*goa.ErrorResponse"
   734  	}
   735  	return codegen.GoTypeRef(t, required, tabs, private)
   736  }
   737  
   738  // decodeGoTypeName handles the case where the type being decoded is a error response media type.
   739  func decodeGoTypeName(t design.DataType, required []string, tabs int, private bool) string {
   740  	mt, ok := t.(*design.MediaTypeDefinition)
   741  	if ok && mt.IsError() {
   742  		return "goa.ErrorResponse"
   743  	}
   744  	return codegen.GoTypeName(t, required, tabs, private)
   745  }
   746  
   747  // cmdFieldType computes the Go type name used to store command flags of the given design type.
   748  func cmdFieldType(t design.DataType, point bool) string {
   749  	var pointer, suffix string
   750  	if point && !t.IsArray() {
   751  		pointer = "*"
   752  	}
   753  	suffix = codegen.GoNativeType(t)
   754  	return pointer + suffix
   755  }
   756  
   757  // cmdFieldTypeString computes the Go type name used to store command flags of the given design type. Complex types are String
   758  func cmdFieldTypeString(t design.DataType, point bool) string {
   759  	var pointer, suffix string
   760  	if point && !t.IsArray() {
   761  		pointer = "*"
   762  	}
   763  	if t.Kind() == design.UUIDKind || t.Kind() == design.DateTimeKind || t.Kind() == design.AnyKind || t.Kind() == design.NumberKind || t.Kind() == design.BooleanKind {
   764  		suffix = "string"
   765  	} else if isArrayOfType(t, design.UUIDKind, design.DateTimeKind, design.AnyKind, design.NumberKind, design.BooleanKind) {
   766  		suffix = "[]string"
   767  	} else {
   768  		suffix = codegen.GoNativeType(t)
   769  	}
   770  	return pointer + suffix
   771  }
   772  
   773  func isArrayOfType(array design.DataType, kinds ...design.Kind) bool {
   774  	if !array.IsArray() {
   775  		return false
   776  	}
   777  	kind := array.ToArray().ElemType.Type.Kind()
   778  	for _, t := range kinds {
   779  		if t == kind {
   780  			return true
   781  		}
   782  	}
   783  	return false
   784  }
   785  
   786  // template used to produce code that serializes arrays of simple values into comma separated
   787  // strings.
   788  var arrayToStringTmpl *template.Template
   789  
   790  // toString generates Go code that converts the given simple type attribute into a string.
   791  func toString(name, target string, att *design.AttributeDefinition) string {
   792  	switch actual := att.Type.(type) {
   793  	case design.Primitive:
   794  		switch actual.Kind() {
   795  		case design.IntegerKind:
   796  			return fmt.Sprintf("%s := strconv.Itoa(%s)", target, name)
   797  		case design.BooleanKind:
   798  			return fmt.Sprintf("%s := strconv.FormatBool(%s)", target, name)
   799  		case design.NumberKind:
   800  			return fmt.Sprintf("%s := strconv.FormatFloat(%s, 'f', -1, 64)", target, name)
   801  		case design.StringKind:
   802  			return fmt.Sprintf("%s := %s", target, name)
   803  		case design.DateTimeKind:
   804  			return fmt.Sprintf("%s := %s.Format(time.RFC3339)", target, strings.Replace(name, "*", "", -1)) // remove pointer if present
   805  		case design.UUIDKind:
   806  			return fmt.Sprintf("%s := %s.String()", target, strings.Replace(name, "*", "", -1)) // remove pointer if present
   807  		case design.AnyKind:
   808  			return fmt.Sprintf("%s := fmt.Sprintf(\"%%v\", %s)", target, name)
   809  		case design.FileKind:
   810  			return fmt.Sprintf("%s := fmt.Sprintf(\"%%v\", %s)", target, name)
   811  		default:
   812  			panic("unknown primitive type")
   813  		}
   814  	case *design.Array:
   815  		data := map[string]interface{}{
   816  			"Name":     name,
   817  			"Target":   target,
   818  			"ElemType": actual.ElemType,
   819  		}
   820  		return codegen.RunTemplate(arrayToStringTmpl, data)
   821  	default:
   822  		panic("cannot convert non simple type " + att.Type.Name() + " to string") // bug
   823  	}
   824  }
   825  
   826  // defaultPath returns the first route path for the given action that does not take any wildcard,
   827  // empty string if none.
   828  func defaultPath(action *design.ActionDefinition) string {
   829  	for _, r := range action.Routes {
   830  		candidate := r.FullPath()
   831  		if !strings.ContainsRune(candidate, ':') {
   832  			return candidate
   833  		}
   834  	}
   835  	return ""
   836  }
   837  
   838  // signerType returns the name of the client signer used for the defined security model on the Action
   839  func signerType(scheme *design.SecuritySchemeDefinition) string {
   840  	switch scheme.Kind {
   841  	case design.JWTSecurityKind:
   842  		return "goaclient.JWTSigner" // goa client package imported under goaclient
   843  	case design.OAuth2SecurityKind:
   844  		return "goaclient.OAuth2Signer"
   845  	case design.APIKeySecurityKind:
   846  		return "goaclient.APIKeySigner"
   847  	case design.BasicAuthSecurityKind:
   848  		return "goaclient.BasicSigner"
   849  	}
   850  	return ""
   851  }
   852  
   853  // pathTemplate returns a fmt format suitable to build a request path to the route.
   854  func pathTemplate(r *design.RouteDefinition) string {
   855  	return design.WildcardRegex.ReplaceAllLiteralString(r.FullPath(), "/%s")
   856  }
   857  
   858  // pathParams return the function signature of the path factory function for the given route.
   859  func pathParams(r *design.RouteDefinition) string {
   860  	pnames := r.Params()
   861  	params := make(design.Object, len(pnames))
   862  	for _, p := range pnames {
   863  		params[p] = r.Parent.Params.Type.ToObject()[p]
   864  	}
   865  	return join(&design.AttributeDefinition{Type: params}, false, pnames)
   866  }
   867  
   868  // typeName returns Go type name of given MediaType definition.
   869  func typeName(mt *design.MediaTypeDefinition) string {
   870  	if mt.IsError() {
   871  		return "ErrorResponse"
   872  	}
   873  	return codegen.GoTypeName(mt, mt.AllRequired(), 1, false)
   874  }
   875  
   876  // initParams returns required and optional paramData extracted from given attribute definition.
   877  func initParams(att *design.AttributeDefinition) ([]*paramData, []*paramData) {
   878  	if att == nil {
   879  		return nil, nil
   880  	}
   881  	obj := att.Type.ToObject()
   882  	var reqParamData []*paramData
   883  	var optParamData []*paramData
   884  	for n, q := range obj {
   885  		varName := codegen.Goify(n, false)
   886  		param := &paramData{
   887  			Name:      n,
   888  			VarName:   varName,
   889  			Attribute: q,
   890  		}
   891  		if q.Type.IsPrimitive() {
   892  			param.MustToString = q.Type.Kind() != design.StringKind
   893  			if att.IsRequired(n) {
   894  				param.ValueName = varName
   895  				reqParamData = append(reqParamData, param)
   896  			} else {
   897  				param.ValueName = "*" + varName
   898  				param.CheckNil = true
   899  				optParamData = append(optParamData, param)
   900  			}
   901  		} else {
   902  			if q.Type.IsArray() {
   903  				param.IsArray = true
   904  				param.ElemAttribute = q.Type.ToArray().ElemType
   905  			}
   906  			param.MustToString = true
   907  			param.ValueName = varName
   908  			param.CheckNil = true
   909  			if att.IsRequired(n) {
   910  				reqParamData = append(reqParamData, param)
   911  			} else {
   912  				optParamData = append(optParamData, param)
   913  			}
   914  		}
   915  	}
   916  
   917  	return reqParamData, optParamData
   918  }
   919  
   920  // paramData is the data structure holding the information needed to generate query params and
   921  // headers handling code.
   922  type paramData struct {
   923  	Name          string
   924  	VarName       string
   925  	ValueName     string
   926  	Attribute     *design.AttributeDefinition
   927  	ElemAttribute *design.AttributeDefinition
   928  	MustToString  bool
   929  	IsArray       bool
   930  	CheckNil      bool
   931  }
   932  
   933  type byParamName []*paramData
   934  
   935  func (b byParamName) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
   936  func (b byParamName) Less(i, j int) bool { return b[i].Name < b[j].Name }
   937  func (b byParamName) Len() int           { return len(b) }
   938  
   939  const (
   940  	arrayToStringT = `	{{ $tmp := tempvar }}{{ $tmp }} := make([]string, len({{ .Name }}))
   941  	for i, e := range {{ .Name }} {
   942  		{{ $tmp2 := tempvar }}{{ toString "e" $tmp2 .ElemType }}
   943  		{{ $tmp }}[i] = {{ $tmp2 }}
   944  	}
   945  	{{ .Target }} := strings.Join({{ $tmp }}, ",")`
   946  
   947  	payloadTmpl = `// {{ gotypename .Payload nil 0 false }} is the {{ .Parent.Name }} {{ .Name }} action payload.
   948  type {{ gotypename .Payload nil 1 false }} {{ gotypedef .Payload 0 true false }}
   949  `
   950  
   951  	typeDecodeTmpl = `{{ $typeName := typeName . }}{{ $funcName := printf "Decode%s" $typeName }}// {{ $funcName }} decodes the {{ $typeName }} instance encoded in resp body.
   952  func (c *Client) {{ $funcName }}(resp *http.Response) ({{ decodegotyperef . .AllRequired 0 false }}, error) {
   953  	var decoded {{ decodegotypename . .AllRequired 0 false }}
   954  	err := c.Decoder.Decode(&decoded, resp.Body, resp.Header.Get("Content-Type"))
   955  	return {{ if .IsObject }}&{{ end }}decoded, err
   956  }
   957  `
   958  
   959  	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") }}{{/*
   960  */}}// {{ $funcName }} computes a request path to the {{ .Route.Parent.Name }} action of {{ .Route.Parent.Parent.Name }}.
   961  func {{ $funcName }}({{ pathParams .Route }}) string {
   962  	{{ range $i, $param := .Params }}{{/*
   963  */}}{{ toString $param.VarName (printf "param%d" $i) $param.Attribute }}
   964  	{{ end }}
   965  	return fmt.Sprintf({{ printf "%q" (pathTemplate .Route) }}{{ range $i, $param := .Params }}, {{ printf "param%d" $i }}{{ end }})
   966  }
   967  `
   968  
   969  	clientsTmpl = `{{ $funcName := goify (printf "%s%s" .Name (title .ResourceName)) true }}{{ $desc := .Description }}{{/*
   970  */}}{{ if $desc }}{{ multiComment $desc }}{{ else }}{{/*
   971  */}}// {{ $funcName }} makes a request to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource{{ end }}
   972  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}{{ if and .HasPayload .HasMultiContent }}, contentType string{{ end }}) (*http.Response, error) {
   973  	req, err := c.New{{ $funcName }}Request(ctx, path{{ if .ParamNames }}, {{ .ParamNames }}{{ end }}{{ if and .HasPayload .HasMultiContent }}, contentType{{ end }})
   974  	if err != nil {
   975  		return nil, err
   976  	}
   977  	return c.Client.Do(ctx, req)
   978  }
   979  `
   980  
   981  	clientsWSTmpl = `{{ $funcName := goify (printf "%s%s" .Name (title .ResourceName)) true }}{{ $desc := .Description }}{{/*
   982  */}}{{ if $desc }}{{ multiComment $desc }}{{ else }}// {{ $funcName }} establishes a websocket connection to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource{{ end }}
   983  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}) (*websocket.Conn, error) {
   984  	scheme := c.Scheme
   985  	if scheme == "" {
   986  		scheme = "{{ .CanonicalScheme }}"
   987  	}
   988  	u := url.URL{Host: c.Host, Scheme: scheme, Path: path}
   989  {{ if .QueryParams }}	values := u.Query()
   990  {{ range .QueryParams }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
   991  	{{ end }}{{/*
   992  
   993  // ARRAY
   994  */}}{{ if .IsArray }}		for _, p := range {{ .VarName }} {
   995  {{ if .MustToString }}{{ $tmp := tempvar }}			{{ toString "p" $tmp .ElemAttribute }}
   996  			values.Add("{{ .Name }}", {{ $tmp }})
   997  {{ else }}			values.Add("{{ .Name }}", {{ .ValueName }})
   998  {{ end }}}{{/*
   999  
  1000  // NON STRING
  1001  */}}{{ else if .MustToString }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
  1002  	values.Set("{{ .Name }}", {{ $tmp }})
  1003  {{/*
  1004  
  1005  // STRING
  1006  */}}{{ else }}	values.Set("{{ .Name }}", {{ .ValueName }})
  1007  {{ end }}{{ if .CheckNil }}	}
  1008  {{ end }}{{ end }}	u.RawQuery = values.Encode()
  1009  {{ end }}	url_ := u.String()
  1010  	cfg, err := websocket.NewConfig(url_, url_)
  1011  	if err != nil {
  1012  		return nil, err
  1013  	}
  1014  {{ range $header := .Headers }}{{ $tmp := tempvar }}	{{ toString $header.VarName $tmp $header.Attribute }}
  1015  	cfg.Header["{{ $header.Name }}"] = []string{ {{ $tmp }} }
  1016  {{ end }}	return websocket.DialConfig(cfg)
  1017  }
  1018  `
  1019  
  1020  	fsTmpl = `// {{ .Name }} downloads {{ if .DirName }}{{ .DirName }}files with the given filename{{ else }}{{ .FileName }}{{ end }} and writes it to the file dest.
  1021  // It returns the number of bytes downloaded in case of success.
  1022  func (c * Client) {{ .Name }}(ctx context.Context, {{ if .DirName }}filename, {{ end }}dest string) (int64, error) {
  1023  	scheme := c.Scheme
  1024  	if scheme == "" {
  1025  		scheme = "{{ .CanonicalScheme }}"
  1026  	}
  1027  {{ if .DirName }}	p := path.Join("{{ .RequestDir }}", filename)
  1028  {{ end }}	u := url.URL{Host: c.Host, Scheme: scheme, Path: {{ if .DirName }}p{{ else }}"{{ .RequestPath }}"{{ end }}}
  1029  	req, err := http.NewRequest("GET", u.String(), nil)
  1030  	if err != nil {
  1031  		return 0, err
  1032  	}
  1033  	resp, err := c.Client.Do(ctx, req)
  1034  	if err != nil {
  1035  		return 0, err
  1036  	}
  1037  	if resp.StatusCode != 200 {
  1038  		var body string
  1039  		if b, err := ioutil.ReadAll(resp.Body); err != nil {
  1040  			if len(b) > 0 {
  1041  				body = ": "+ string(b)
  1042  			}
  1043  		}
  1044  		return 0, fmt.Errorf("%s%s", resp.Status, body)
  1045  	}
  1046  	defer resp.Body.Close()
  1047  	out, err := os.Create(dest)
  1048  	if err != nil {
  1049  		return 0, err
  1050  	}
  1051  	defer out.Close()
  1052  	return io.Copy(out, resp.Body)
  1053  }
  1054  `
  1055  
  1056  	requestsTmpl = `{{ $funcName := goify (printf "New%s%sRequest" (title .Name) (title .ResourceName)) true }}{{/*
  1057  */}}// {{ $funcName }} create the request corresponding to the {{ .Name }} action endpoint of the {{ .ResourceName }} resource.
  1058  func (c *Client) {{ $funcName }}(ctx context.Context, path string{{ if .Params }}, {{ .Params }}{{ end }}{{ if .HasPayload }}{{ if .HasMultiContent }}, contentType string{{ end }}{{ end }}) (*http.Request, error) {
  1059  {{ if .HasPayload }}	var body bytes.Buffer
  1060  {{ if .PayloadMultipart }}	w := multipart.NewWriter(&body)
  1061  {{ $o := .Payload.ToObject }}{{ range $name, $att := $o }}{{ if eq $att.Type.Kind 13 }}{{/*
  1062  */}}	{
  1063  		_, file := filepath.Split({{ printf "payload.%s" (goify $name true) }})
  1064  		fw, err := w.CreateFormFile("{{ $name }}", file)
  1065  		if err != nil {
  1066  			return nil, err
  1067  		}
  1068  		fh, err := os.Open({{ printf "payload.%s" (goify $name true) }})
  1069  		if err != nil {
  1070  			return nil, err
  1071  		}
  1072  		defer fh.Close()
  1073  		if _, err := io.Copy(fw, fh); err != nil {
  1074  			return nil, err
  1075  		}
  1076  	}
  1077  {{ else }}	{
  1078  		fw, err := w.CreateFormField("{{ $name }}")
  1079  		if err != nil {
  1080  			return nil, err
  1081  		}
  1082  		{{ toString (printf "payload.%s" (goify $name true)) "s" $att }}
  1083  		if _, err := fw.Write([]byte(s)); err != nil {
  1084  			return nil, err
  1085  		}
  1086  	}
  1087  {{ end }}{{ end }}	if err := w.Close(); err != nil {
  1088  		return nil, err
  1089  	}
  1090  {{ else }}{{ if .HasMultiContent }}	if contentType == "" {
  1091  		contentType = "*/*" // Use default encoder
  1092  	}
  1093  {{ end }}	err := c.Encoder.Encode(payload, &body, {{ if .HasMultiContent }}contentType{{ else }}"*/*"{{ end }})
  1094  	if err != nil {
  1095  		return nil, fmt.Errorf("failed to encode body: %s", err)
  1096  	}
  1097  {{ end }}{{ end }}	scheme := c.Scheme
  1098  	if scheme == "" {
  1099  		scheme = "{{ .CanonicalScheme }}"
  1100  	}
  1101  	u := url.URL{Host: c.Host, Scheme: scheme, Path: path}
  1102  {{ if .QueryParams }}	values := u.Query()
  1103  {{ range .QueryParams }}{{/*
  1104  
  1105  // ARRAY
  1106  */}}{{ if .IsArray }}		for _, p := range {{ .VarName }} {
  1107  {{ if .MustToString }}{{ $tmp := tempvar }}			{{ toString "p" $tmp .ElemAttribute }}
  1108  			values.Add("{{ .Name }}", {{ $tmp }})
  1109  {{ else }}			values.Add("{{ .Name }}", {{ .ValueName }})
  1110  {{ end }}	 }
  1111  {{/*
  1112  
  1113  // NON STRING
  1114  */}}{{ else if .MustToString }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1115  	{{ end }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
  1116  	values.Set("{{ .Name }}", {{ $tmp }})
  1117  {{ if .CheckNil }}	}
  1118  {{ end }}{{/*
  1119  
  1120  // STRING
  1121  */}}{{ else }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1122  	{{ end }}	values.Set("{{ .Name }}", {{ .ValueName }})
  1123  {{ if .CheckNil }}	}
  1124  {{ end }}{{ end }}{{ end }}	u.RawQuery = values.Encode()
  1125  {{ end }}{{ if .HasPayload }}	req, err := http.NewRequest({{ $route := index .Routes 0 }}"{{ $route.Verb }}", u.String(), &body)
  1126  {{ else }}	req, err := http.NewRequest({{ $route := index .Routes 0 }}"{{ $route.Verb }}", u.String(), nil)
  1127  {{ end }}	if err != nil {
  1128  		return nil, err
  1129  	}
  1130  {{ if or .HasPayload .Headers }}	header := req.Header
  1131  {{ if .PayloadMultipart }}	header.Set("Content-Type", w.FormDataContentType())
  1132  {{ else }}{{ if .HasPayload }}{{ if .HasMultiContent }}	if contentType == "*/*" {
  1133  		header.Set("Content-Type", "{{ .DefaultContentType }}")
  1134  	} else {
  1135  		header.Set("Content-Type", contentType)
  1136  	}
  1137  {{ else }}	header.Set("Content-Type", "{{ .DefaultContentType }}")
  1138  {{ end }}{{ end }}{{ end }}{{ range .Headers }}{{ if .CheckNil }}	if {{ .VarName }} != nil {
  1139  {{ end }}{{ if .MustToString }}{{ $tmp := tempvar }}	{{ toString .ValueName $tmp .Attribute }}
  1140  	header.Set("{{ .Name }}", {{ $tmp }}){{ else }}
  1141  	header.Set("{{ .Name }}", {{ .ValueName }})
  1142  {{ end }}{{ if .CheckNil }}	}{{ end }}
  1143  {{ end }}{{ end }}{{ if .Signer }}	if c.{{ .Signer }}Signer != nil {
  1144  		if err := c.{{ .Signer }}Signer.Sign(req); err != nil {
  1145  			return nil, err
  1146  		}
  1147  	}
  1148  {{ end }}	return req, nil
  1149  }
  1150  `
  1151  
  1152  	clientTmpl = `// Client is the {{ .API.Name }} service client.
  1153  type Client struct {
  1154  	*goaclient.Client{{range $security := .API.SecuritySchemes }}{{ $signer := signerType $security }}{{ if $signer }}
  1155  	{{ goify $security.SchemeName true }}Signer goaclient.Signer{{ end }}{{ end }}
  1156  	Encoder *goa.HTTPEncoder
  1157  	Decoder *goa.HTTPDecoder
  1158  }
  1159  
  1160  // New instantiates the client.
  1161  func New(c goaclient.Doer) *Client {
  1162  	client := &Client{
  1163  		Client: goaclient.New(c),
  1164  		Encoder: goa.NewHTTPEncoder(),
  1165  		Decoder: goa.NewHTTPDecoder(),
  1166  	}
  1167  
  1168  {{ if .Encoders }}	// Setup encoders and decoders
  1169  {{ range .Encoders }}{{/*
  1170  */}}	client.Encoder.Register({{ .PackageName }}.{{ .Function }}, "{{ joinStrings .MIMETypes "\", \"" }}")
  1171  {{ end }}{{ range .Decoders }}{{/*
  1172  */}}	client.Decoder.Register({{ .PackageName }}.{{ .Function }}, "{{ joinStrings .MIMETypes "\", \"" }}")
  1173  {{ end }}
  1174  
  1175  	// Setup default encoder and decoder
  1176  {{ range .Encoders }}{{ if .Default }}{{/*
  1177  */}}	client.Encoder.Register({{ .PackageName }}.{{ .Function }}, "*/*")
  1178  {{ end }}{{ end }}{{ range .Decoders }}{{ if .Default }}{{/*
  1179  */}}	client.Decoder.Register({{ .PackageName }}.{{ .Function }}, "*/*")
  1180  {{ end }}{{ end }}
  1181  {{ end }}	return client
  1182  }
  1183  
  1184  {{range $security := .API.SecuritySchemes }}{{ $signer := signerType $security }}{{ if $signer }}{{/*
  1185  */}}{{ $name := printf "%sSigner" (goify $security.SchemeName true) }}{{/*
  1186  */}}// Set{{ $name }} sets the request signer for the {{ $security.SchemeName }} security scheme.
  1187  func (c *Client) Set{{ $name }}(signer goaclient.Signer) {
  1188  	c.{{ $name }} = signer
  1189  }
  1190  {{ end }}{{ end }}
  1191  `
  1192  )