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