1// Copyright 2019 DeepMap, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package codegen
15
16import (
17	"bufio"
18	"bytes"
19	"fmt"
20	"strings"
21	"text/template"
22	"unicode"
23
24	"github.com/getkin/kin-openapi/openapi3"
25	"github.com/pkg/errors"
26)
27
28type ParameterDefinition struct {
29	ParamName string // The original json parameter name, eg param_name
30	In        string // Where the parameter is defined - path, header, cookie, query
31	Required  bool   // Is this a required parameter?
32	Spec      *openapi3.Parameter
33	Schema    Schema
34}
35
36// This function is here as an adapter after a large refactoring so that I don't
37// have to update all the templates. It returns the type definition for a parameter,
38// without the leading '*' for optional ones.
39func (pd ParameterDefinition) TypeDef() string {
40	typeDecl := pd.Schema.TypeDecl()
41	return typeDecl
42}
43
44// Generate the JSON annotation to map GoType to json type name. If Parameter
45// Foo is marshaled to json as "foo", this will create the annotation
46// 'json:"foo"'
47func (pd *ParameterDefinition) JsonTag() string {
48	if pd.Required {
49		return fmt.Sprintf("`json:\"%s\"`", pd.ParamName)
50	} else {
51		return fmt.Sprintf("`json:\"%s,omitempty\"`", pd.ParamName)
52	}
53}
54
55func (pd *ParameterDefinition) IsJson() bool {
56	p := pd.Spec
57	if len(p.Content) == 1 {
58		_, found := p.Content["application/json"]
59		return found
60	}
61	return false
62}
63
64func (pd *ParameterDefinition) IsPassThrough() bool {
65	p := pd.Spec
66	if len(p.Content) > 1 {
67		return true
68	}
69	if len(p.Content) == 1 {
70		return !pd.IsJson()
71	}
72	return false
73}
74
75func (pd *ParameterDefinition) IsStyled() bool {
76	p := pd.Spec
77	return p.Schema != nil
78}
79
80func (pd *ParameterDefinition) Style() string {
81	style := pd.Spec.Style
82	if style == "" {
83		in := pd.Spec.In
84		switch in {
85		case "path", "header":
86			return "simple"
87		case "query", "cookie":
88			return "form"
89		default:
90			panic("unknown parameter format")
91		}
92	}
93	return style
94}
95
96func (pd *ParameterDefinition) Explode() bool {
97	if pd.Spec.Explode == nil {
98		in := pd.Spec.In
99		switch in {
100		case "path", "header":
101			return false
102		case "query", "cookie":
103			return true
104		default:
105			panic("unknown parameter format")
106		}
107	}
108	return *pd.Spec.Explode
109}
110
111func (pd ParameterDefinition) GoVariableName() string {
112	name := LowercaseFirstCharacter(pd.GoName())
113	if IsGoKeyword(name) {
114		name = "p" + UppercaseFirstCharacter(name)
115	}
116	if unicode.IsNumber([]rune(name)[0]) {
117		name = "n" + name
118	}
119	return name
120}
121
122func (pd ParameterDefinition) GoName() string {
123	return SchemaNameToTypeName(pd.ParamName)
124}
125
126func (pd ParameterDefinition) IndirectOptional() bool {
127	return !pd.Required && !pd.Schema.SkipOptionalPointer
128}
129
130type ParameterDefinitions []ParameterDefinition
131
132func (p ParameterDefinitions) FindByName(name string) *ParameterDefinition {
133	for _, param := range p {
134		if param.ParamName == name {
135			return &param
136		}
137	}
138	return nil
139}
140
141// This function walks the given parameters dictionary, and generates the above
142// descriptors into a flat list. This makes it a lot easier to traverse the
143// data in the template engine.
144func DescribeParameters(params openapi3.Parameters, path []string) ([]ParameterDefinition, error) {
145	outParams := make([]ParameterDefinition, 0)
146	for _, paramOrRef := range params {
147		param := paramOrRef.Value
148
149		goType, err := paramToGoType(param, append(path, param.Name))
150		if err != nil {
151			return nil, fmt.Errorf("error generating type for param (%s): %s",
152				param.Name, err)
153		}
154
155		pd := ParameterDefinition{
156			ParamName: param.Name,
157			In:        param.In,
158			Required:  param.Required,
159			Spec:      param,
160			Schema:    goType,
161		}
162
163		// If this is a reference to a predefined type, simply use the reference
164		// name as the type. $ref: "#/components/schemas/custom_type" becomes
165		// "CustomType".
166		if IsGoTypeReference(paramOrRef.Ref) {
167			goType, err := RefPathToGoType(paramOrRef.Ref)
168			if err != nil {
169				return nil, fmt.Errorf("error dereferencing (%s) for param (%s): %s",
170					paramOrRef.Ref, param.Name, err)
171			}
172			pd.Schema.GoType = goType
173		}
174		outParams = append(outParams, pd)
175	}
176	return outParams, nil
177}
178
179type SecurityDefinition struct {
180	ProviderName string
181	Scopes       []string
182}
183
184func DescribeSecurityDefinition(securityRequirements openapi3.SecurityRequirements) []SecurityDefinition {
185	outDefs := make([]SecurityDefinition, 0)
186
187	for _, sr := range securityRequirements {
188		for _, k := range SortedSecurityRequirementKeys(sr) {
189			v := sr[k]
190			outDefs = append(outDefs, SecurityDefinition{ProviderName: k, Scopes: v})
191		}
192	}
193
194	return outDefs
195}
196
197// This structure describes an Operation
198type OperationDefinition struct {
199	OperationId string // The operation_id description from Swagger, used to generate function names
200
201	PathParams          []ParameterDefinition // Parameters in the path, eg, /path/:param
202	HeaderParams        []ParameterDefinition // Parameters in HTTP headers
203	QueryParams         []ParameterDefinition // Parameters in the query, /path?param
204	CookieParams        []ParameterDefinition // Parameters in cookies
205	TypeDefinitions     []TypeDefinition      // These are all the types we need to define for this operation
206	SecurityDefinitions []SecurityDefinition  // These are the security providers
207	BodyRequired        bool
208	Bodies              []RequestBodyDefinition // The list of bodies for which to generate handlers.
209	Summary             string                  // Summary string from Swagger, used to generate a comment
210	Method              string                  // GET, POST, DELETE, etc.
211	Path                string                  // The Swagger path for the operation, like /resource/{id}
212	Spec                *openapi3.Operation
213}
214
215// Returns the list of all parameters except Path parameters. Path parameters
216// are handled differently from the rest, since they're mandatory.
217func (o *OperationDefinition) Params() []ParameterDefinition {
218	result := append(o.QueryParams, o.HeaderParams...)
219	result = append(result, o.CookieParams...)
220	return result
221}
222
223// Returns all parameters
224func (o *OperationDefinition) AllParams() []ParameterDefinition {
225	result := append(o.QueryParams, o.HeaderParams...)
226	result = append(result, o.CookieParams...)
227	result = append(result, o.PathParams...)
228	return result
229}
230
231// If we have parameters other than path parameters, they're bundled into an
232// object. Returns true if we have any of those. This is used from the template
233// engine.
234func (o *OperationDefinition) RequiresParamObject() bool {
235	return len(o.Params()) > 0
236}
237
238// This is called by the template engine to determine whether to generate body
239// marshaling code on the client. This is true for all body types, whether or
240// not we generate types for them.
241func (o *OperationDefinition) HasBody() bool {
242	return o.Spec.RequestBody != nil
243}
244
245// This returns the Operations summary as a multi line comment
246func (o *OperationDefinition) SummaryAsComment() string {
247	if o.Summary == "" {
248		return ""
249	}
250	trimmed := strings.TrimSuffix(o.Summary, "\n")
251	parts := strings.Split(trimmed, "\n")
252	for i, p := range parts {
253		parts[i] = "// " + p
254	}
255	return strings.Join(parts, "\n")
256}
257
258// Produces a list of type definitions for a given Operation for the response
259// types which we know how to parse. These will be turned into fields on a
260// response object for automatic deserialization of responses in the generated
261// Client code. See "client-with-responses.tmpl".
262func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefinition, error) {
263	var tds []ResponseTypeDefinition
264
265	responses := o.Spec.Responses
266	sortedResponsesKeys := SortedResponsesKeys(responses)
267	for _, responseName := range sortedResponsesKeys {
268		responseRef := responses[responseName]
269
270		// We can only generate a type if we have a value:
271		if responseRef.Value != nil {
272			sortedContentKeys := SortedContentKeys(responseRef.Value.Content)
273			for _, contentTypeName := range sortedContentKeys {
274				contentType := responseRef.Value.Content[contentTypeName]
275				// We can only generate a type if we have a schema:
276				if contentType.Schema != nil {
277					responseSchema, err := GenerateGoSchema(contentType.Schema, []string{responseName})
278					if err != nil {
279						return nil, errors.Wrap(err, fmt.Sprintf("Unable to determine Go type for %s.%s", o.OperationId, contentTypeName))
280					}
281
282					var typeName string
283					switch {
284					case StringInArray(contentTypeName, contentTypesJSON):
285						typeName = fmt.Sprintf("JSON%s", ToCamelCase(responseName))
286					// YAML:
287					case StringInArray(contentTypeName, contentTypesYAML):
288						typeName = fmt.Sprintf("YAML%s", ToCamelCase(responseName))
289					// XML:
290					case StringInArray(contentTypeName, contentTypesXML):
291						typeName = fmt.Sprintf("XML%s", ToCamelCase(responseName))
292					default:
293						continue
294					}
295
296					td := ResponseTypeDefinition{
297						TypeDefinition: TypeDefinition{
298							TypeName: typeName,
299							Schema:   responseSchema,
300						},
301						ResponseName:    responseName,
302						ContentTypeName: contentTypeName,
303					}
304					if IsGoTypeReference(contentType.Schema.Ref) {
305						refType, err := RefPathToGoType(contentType.Schema.Ref)
306						if err != nil {
307							return nil, errors.Wrap(err, "error dereferencing response Ref")
308						}
309						td.Schema.RefType = refType
310					}
311					tds = append(tds, td)
312				}
313			}
314		}
315	}
316	return tds, nil
317}
318
319// This describes a request body
320type RequestBodyDefinition struct {
321	// Is this body required, or optional?
322	Required bool
323
324	// This is the schema describing this body
325	Schema Schema
326
327	// When we generate type names, we need a Tag for it, such as JSON, in
328	// which case we will produce "JSONBody".
329	NameTag string
330
331	// This is the content type corresponding to the body, eg, application/json
332	ContentType string
333
334	// Whether this is the default body type. For an operation named OpFoo, we
335	// will not add suffixes like OpFooJSONBody for this one.
336	Default bool
337}
338
339// Returns the Go type definition for a request body
340func (r RequestBodyDefinition) TypeDef(opID string) *TypeDefinition {
341	return &TypeDefinition{
342		TypeName: fmt.Sprintf("%s%sRequestBody", opID, r.NameTag),
343		Schema:   r.Schema,
344	}
345}
346
347// Returns whether the body is a custom inline type, or pre-defined. This is
348// poorly named, but it's here for compatibility reasons post-refactoring
349// TODO: clean up the templates code, it can be simpler.
350func (r RequestBodyDefinition) CustomType() bool {
351	return r.Schema.RefType == ""
352}
353
354// When we're generating multiple functions which relate to request bodies,
355// this generates the suffix. Such as Operation DoFoo would be suffixed with
356// DoFooWithXMLBody.
357func (r RequestBodyDefinition) Suffix() string {
358	// The default response is never suffixed.
359	if r.Default {
360		return ""
361	}
362	return "With" + r.NameTag + "Body"
363}
364
365// This function returns the subset of the specified parameters which are of the
366// specified type.
367func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []ParameterDefinition {
368	var out []ParameterDefinition
369	for _, p := range params {
370		if p.In == in {
371			out = append(out, p)
372		}
373	}
374	return out
375}
376
377// OperationDefinitions returns all operations for a swagger definition.
378func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
379	var operations []OperationDefinition
380
381	for _, requestPath := range SortedPathsKeys(swagger.Paths) {
382		pathItem := swagger.Paths[requestPath]
383		// These are parameters defined for all methods on a given path. They
384		// are shared by all methods.
385		globalParams, err := DescribeParameters(pathItem.Parameters, nil)
386		if err != nil {
387			return nil, fmt.Errorf("error describing global parameters for %s: %s",
388				requestPath, err)
389		}
390
391		// Each path can have a number of operations, POST, GET, OPTIONS, etc.
392		pathOps := pathItem.Operations()
393		for _, opName := range SortedOperationsKeys(pathOps) {
394			op := pathOps[opName]
395			if pathItem.Servers != nil {
396				op.Servers = &pathItem.Servers
397			}
398			// We rely on OperationID to generate function names, it's required
399			if op.OperationID == "" {
400				op.OperationID, err = generateDefaultOperationID(opName, requestPath)
401				if err != nil {
402					return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s",
403						opName, requestPath, err)
404				}
405				op.OperationID = op.OperationID
406			} else {
407				op.OperationID = ToCamelCase(op.OperationID)
408			}
409
410			// These are parameters defined for the specific path method that
411			// we're iterating over.
412			localParams, err := DescribeParameters(op.Parameters, []string{op.OperationID + "Params"})
413			if err != nil {
414				return nil, fmt.Errorf("error describing global parameters for %s/%s: %s",
415					opName, requestPath, err)
416			}
417			// All the parameters required by a handler are the union of the
418			// global parameters and the local parameters.
419			allParams := append(globalParams, localParams...)
420
421			// Order the path parameters to match the order as specified in
422			// the path, not in the swagger spec, and validate that the parameter
423			// names match, as downstream code depends on that.
424			pathParams := FilterParameterDefinitionByType(allParams, "path")
425			pathParams, err = SortParamsByPath(requestPath, pathParams)
426			if err != nil {
427				return nil, err
428			}
429
430			bodyDefinitions, typeDefinitions, err := GenerateBodyDefinitions(op.OperationID, op.RequestBody)
431			if err != nil {
432				return nil, errors.Wrap(err, "error generating body definitions")
433			}
434
435			opDef := OperationDefinition{
436				PathParams:   pathParams,
437				HeaderParams: FilterParameterDefinitionByType(allParams, "header"),
438				QueryParams:  FilterParameterDefinitionByType(allParams, "query"),
439				CookieParams: FilterParameterDefinitionByType(allParams, "cookie"),
440				OperationId:  ToCamelCase(op.OperationID),
441				// Replace newlines in summary.
442				Summary:         op.Summary,
443				Method:          opName,
444				Path:            requestPath,
445				Spec:            op,
446				Bodies:          bodyDefinitions,
447				TypeDefinitions: typeDefinitions,
448			}
449
450			// check for overrides of SecurityDefinitions.
451			// See: "Step 2. Applying security:" from the spec:
452			// https://swagger.io/docs/specification/authentication/
453			if op.Security != nil {
454				opDef.SecurityDefinitions = DescribeSecurityDefinition(*op.Security)
455			} else {
456				// use global securityDefinitions
457				// globalSecurityDefinitions contains the top-level securityDefinitions.
458				// They are the default securityPermissions which are injected into each
459				// path, except for the case where a path explicitly overrides them.
460				opDef.SecurityDefinitions = DescribeSecurityDefinition(swagger.Security)
461
462			}
463
464			if op.RequestBody != nil {
465				opDef.BodyRequired = op.RequestBody.Value.Required
466			}
467
468			// Generate all the type definitions needed for this operation
469			opDef.TypeDefinitions = append(opDef.TypeDefinitions, GenerateTypeDefsForOperation(opDef)...)
470
471			operations = append(operations, opDef)
472		}
473	}
474	return operations, nil
475}
476
477func generateDefaultOperationID(opName string, requestPath string) (string, error) {
478	var operationId string = strings.ToLower(opName)
479
480	if opName == "" {
481		return "", fmt.Errorf("operation name cannot be an empty string")
482	}
483
484	if requestPath == "" {
485		return "", fmt.Errorf("request path cannot be an empty string")
486	}
487
488	for _, part := range strings.Split(requestPath, "/") {
489		if part != "" {
490			operationId = operationId + "-" + part
491		}
492	}
493
494	return ToCamelCase(operationId), nil
495}
496
497// This function turns the Swagger body definitions into a list of our body
498// definitions which will be used for code generation.
499func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBodyRef) ([]RequestBodyDefinition, []TypeDefinition, error) {
500	if bodyOrRef == nil {
501		return nil, nil, nil
502	}
503	body := bodyOrRef.Value
504
505	var bodyDefinitions []RequestBodyDefinition
506	var typeDefinitions []TypeDefinition
507
508	for contentType, content := range body.Content {
509		var tag string
510		var defaultBody bool
511
512		switch contentType {
513		case "application/json":
514			tag = "JSON"
515			defaultBody = true
516		default:
517			continue
518		}
519
520		bodyTypeName := operationID + tag + "Body"
521		bodySchema, err := GenerateGoSchema(content.Schema, []string{bodyTypeName})
522		if err != nil {
523			return nil, nil, errors.Wrap(err, "error generating request body definition")
524		}
525
526		// If the body is a pre-defined type
527		if IsGoTypeReference(bodyOrRef.Ref) {
528			// Convert the reference path to Go type
529			refType, err := RefPathToGoType(bodyOrRef.Ref)
530			if err != nil {
531				return nil, nil, errors.Wrap(err, fmt.Sprintf("error turning reference (%s) into a Go type", bodyOrRef.Ref))
532			}
533			bodySchema.RefType = refType
534		}
535
536		// If the request has a body, but it's not a user defined
537		// type under #/components, we'll define a type for it, so
538		// that we have an easy to use type for marshaling.
539		if bodySchema.RefType == "" {
540			td := TypeDefinition{
541				TypeName: bodyTypeName,
542				Schema:   bodySchema,
543			}
544			typeDefinitions = append(typeDefinitions, td)
545			// The body schema now is a reference to a type
546			bodySchema.RefType = bodyTypeName
547		}
548
549		bd := RequestBodyDefinition{
550			Required:    body.Required,
551			Schema:      bodySchema,
552			NameTag:     tag,
553			ContentType: contentType,
554			Default:     defaultBody,
555		}
556		bodyDefinitions = append(bodyDefinitions, bd)
557	}
558	return bodyDefinitions, typeDefinitions, nil
559}
560
561func GenerateTypeDefsForOperation(op OperationDefinition) []TypeDefinition {
562	var typeDefs []TypeDefinition
563	// Start with the params object itself
564	if len(op.Params()) != 0 {
565		typeDefs = append(typeDefs, GenerateParamsTypes(op)...)
566	}
567
568	// Now, go through all the additional types we need to declare.
569	for _, param := range op.AllParams() {
570		typeDefs = append(typeDefs, param.Schema.GetAdditionalTypeDefs()...)
571	}
572
573	for _, body := range op.Bodies {
574		typeDefs = append(typeDefs, body.Schema.GetAdditionalTypeDefs()...)
575	}
576	return typeDefs
577}
578
579// This defines the schema for a parameters definition object which encapsulates
580// all the query, header and cookie parameters for an operation.
581func GenerateParamsTypes(op OperationDefinition) []TypeDefinition {
582	var typeDefs []TypeDefinition
583
584	objectParams := op.QueryParams
585	objectParams = append(objectParams, op.HeaderParams...)
586	objectParams = append(objectParams, op.CookieParams...)
587
588	typeName := op.OperationId + "Params"
589
590	s := Schema{}
591	for _, param := range objectParams {
592		pSchema := param.Schema
593		if pSchema.HasAdditionalProperties {
594			propRefName := strings.Join([]string{typeName, param.GoName()}, "_")
595			pSchema.RefType = propRefName
596			typeDefs = append(typeDefs, TypeDefinition{
597				TypeName: propRefName,
598				Schema:   param.Schema,
599			})
600		}
601		prop := Property{
602			Description:    param.Spec.Description,
603			JsonFieldName:  param.ParamName,
604			Required:       param.Required,
605			Schema:         pSchema,
606			ExtensionProps: &param.Spec.ExtensionProps,
607		}
608		s.Properties = append(s.Properties, prop)
609	}
610
611	s.Description = op.Spec.Description
612	s.GoType = GenStructFromSchema(s)
613
614	td := TypeDefinition{
615		TypeName: typeName,
616		Schema:   s,
617	}
618	return append(typeDefs, td)
619}
620
621// Generates code for all types produced
622func GenerateTypesForOperations(t *template.Template, ops []OperationDefinition) (string, error) {
623	var buf bytes.Buffer
624	w := bufio.NewWriter(&buf)
625
626	err := t.ExecuteTemplate(w, "param-types.tmpl", ops)
627	if err != nil {
628		return "", errors.Wrap(err, "error generating types for params objects")
629	}
630
631	err = t.ExecuteTemplate(w, "request-bodies.tmpl", ops)
632	if err != nil {
633		return "", errors.Wrap(err, "error generating request bodies for operations")
634	}
635
636	// Generate boiler plate for all additional types.
637	var td []TypeDefinition
638	for _, op := range ops {
639		td = append(td, op.TypeDefinitions...)
640	}
641
642	addProps, err := GenerateAdditionalPropertyBoilerplate(t, td)
643	if err != nil {
644		return "", errors.Wrap(err, "error generating additional properties boilerplate for operations")
645	}
646
647	_, err = w.WriteString("\n")
648	if err != nil {
649		return "", errors.Wrap(err, "error generating additional properties boilerplate for operations")
650	}
651
652	_, err = w.WriteString(addProps)
653	if err != nil {
654		return "", errors.Wrap(err, "error generating additional properties boilerplate for operations")
655	}
656
657	err = w.Flush()
658	if err != nil {
659		return "", errors.Wrap(err, "error flushing output buffer for server interface")
660	}
661
662	return buf.String(), nil
663}
664
665// GenerateChiServer This function generates all the go code for the ServerInterface as well as
666// all the wrapper functions around our handlers.
667func GenerateChiServer(t *template.Template, operations []OperationDefinition) (string, error) {
668	var buf bytes.Buffer
669	w := bufio.NewWriter(&buf)
670
671	err := t.ExecuteTemplate(w, "chi-interface.tmpl", operations)
672	if err != nil {
673		return "", errors.Wrap(err, "error generating server interface")
674	}
675
676	err = t.ExecuteTemplate(w, "chi-middleware.tmpl", operations)
677	if err != nil {
678		return "", errors.Wrap(err, "error generating server middleware")
679	}
680
681	err = t.ExecuteTemplate(w, "chi-handler.tmpl", operations)
682	if err != nil {
683		return "", errors.Wrap(err, "error generating server http handler")
684	}
685
686	err = w.Flush()
687	if err != nil {
688		return "", errors.Wrap(err, "error flushing output buffer for server")
689	}
690
691	return buf.String(), nil
692}
693
694// GenerateEchoServer This function generates all the go code for the ServerInterface as well as
695// all the wrapper functions around our handlers.
696func GenerateEchoServer(t *template.Template, operations []OperationDefinition) (string, error) {
697	si, err := GenerateServerInterface(t, operations)
698	if err != nil {
699		return "", fmt.Errorf("Error generating server types and interface: %s", err)
700	}
701
702	wrappers, err := GenerateWrappers(t, operations)
703	if err != nil {
704		return "", fmt.Errorf("Error generating handler wrappers: %s", err)
705	}
706
707	register, err := GenerateRegistration(t, operations)
708	if err != nil {
709		return "", fmt.Errorf("Error generating handler registration: %s", err)
710	}
711	return strings.Join([]string{si, wrappers, register}, "\n"), nil
712}
713
714// Uses the template engine to generate the server interface
715func GenerateServerInterface(t *template.Template, ops []OperationDefinition) (string, error) {
716	var buf bytes.Buffer
717	w := bufio.NewWriter(&buf)
718
719	err := t.ExecuteTemplate(w, "server-interface.tmpl", ops)
720
721	if err != nil {
722		return "", fmt.Errorf("error generating server interface: %s", err)
723	}
724	err = w.Flush()
725	if err != nil {
726		return "", fmt.Errorf("error flushing output buffer for server interface: %s", err)
727	}
728	return buf.String(), nil
729}
730
731// Uses the template engine to generate all the wrappers which wrap our simple
732// interface functions and perform marshallin/unmarshalling from HTTP
733// request objects.
734func GenerateWrappers(t *template.Template, ops []OperationDefinition) (string, error) {
735	var buf bytes.Buffer
736	w := bufio.NewWriter(&buf)
737
738	err := t.ExecuteTemplate(w, "wrappers.tmpl", ops)
739
740	if err != nil {
741		return "", fmt.Errorf("error generating server interface: %s", err)
742	}
743	err = w.Flush()
744	if err != nil {
745		return "", fmt.Errorf("error flushing output buffer for server interface: %s", err)
746	}
747	return buf.String(), nil
748}
749
750// Uses the template engine to generate the function which registers our wrappers
751// as Echo path handlers.
752func GenerateRegistration(t *template.Template, ops []OperationDefinition) (string, error) {
753	var buf bytes.Buffer
754	w := bufio.NewWriter(&buf)
755
756	err := t.ExecuteTemplate(w, "register.tmpl", ops)
757
758	if err != nil {
759		return "", fmt.Errorf("error generating route registration: %s", err)
760	}
761	err = w.Flush()
762	if err != nil {
763		return "", fmt.Errorf("error flushing output buffer for route registration: %s", err)
764	}
765	return buf.String(), nil
766}
767
768// Uses the template engine to generate the function which registers our wrappers
769// as Echo path handlers.
770func GenerateClient(t *template.Template, ops []OperationDefinition) (string, error) {
771	var buf bytes.Buffer
772	w := bufio.NewWriter(&buf)
773
774	err := t.ExecuteTemplate(w, "client.tmpl", ops)
775
776	if err != nil {
777		return "", fmt.Errorf("error generating client bindings: %s", err)
778	}
779	err = w.Flush()
780	if err != nil {
781		return "", fmt.Errorf("error flushing output buffer for client: %s", err)
782	}
783	return buf.String(), nil
784}
785
786// This generates a client which extends the basic client which does response
787// unmarshaling.
788func GenerateClientWithResponses(t *template.Template, ops []OperationDefinition) (string, error) {
789	var buf bytes.Buffer
790	w := bufio.NewWriter(&buf)
791
792	err := t.ExecuteTemplate(w, "client-with-responses.tmpl", ops)
793
794	if err != nil {
795		return "", fmt.Errorf("error generating client bindings: %s", err)
796	}
797	err = w.Flush()
798	if err != nil {
799		return "", fmt.Errorf("error flushing output buffer for client: %s", err)
800	}
801	return buf.String(), nil
802}
803