1// Copyright 2015 go-swagger maintainers
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.
14
15package generator
16
17import (
18	"encoding/json"
19	"errors"
20	"fmt"
21	"os"
22	"path/filepath"
23	"sort"
24
25	"github.com/go-openapi/analysis"
26	"github.com/go-openapi/runtime"
27	"github.com/go-openapi/loads"
28	"github.com/go-openapi/swag"
29)
30
31// GenerateClient generates a client library for a swagger spec document.
32func GenerateClient(name string, modelNames, operationIDs []string, opts *GenOpts) error {
33	if opts == nil {
34		return errors.New("gen opts are required")
35	}
36	if err := opts.EnsureDefaults(true); err != nil {
37		return err
38	}
39
40	if opts.TemplateDir != "" {
41		if err := templates.LoadDir(opts.TemplateDir); err != nil {
42			return err
43		}
44	}
45
46	// Load the spec
47	var err error
48	var specDoc *loads.Document
49	opts.Spec, specDoc, err = loadSpec(opts.Spec)
50	if err != nil {
51		return err
52	}
53
54	// Validate if needed
55	if opts.ValidateSpec {
56		if err = validateSpec(opts.Spec, specDoc); err != nil {
57			return err
58		}
59	}
60
61	analyzed := analysis.New(specDoc.Spec())
62
63	models, err := gatherModels(specDoc, modelNames)
64	if err != nil {
65		return err
66	}
67	operations := gatherOperations(analyzed, operationIDs)
68
69	defaultScheme := opts.DefaultScheme
70	if defaultScheme == "" {
71		defaultScheme = sHTTP
72	}
73
74	defaultConsumes := opts.DefaultConsumes
75	if defaultConsumes == "" {
76		defaultConsumes = runtime.JSONMime
77	}
78
79	defaultProduces := opts.DefaultProduces
80	if defaultProduces == "" {
81		defaultProduces = runtime.JSONMime
82	}
83
84	generator := appGenerator{
85		Name:            appNameOrDefault(specDoc, name, "rest"),
86		SpecDoc:         specDoc,
87		Analyzed:        analyzed,
88		Models:          models,
89		Operations:      operations,
90		Target:          opts.Target,
91		DumpData:        opts.DumpData,
92		Package:         opts.LanguageOpts.MangleName(swag.ToFileName(opts.ClientPackage), "client"),
93		APIPackage:      opts.LanguageOpts.MangleName(swag.ToFileName(opts.APIPackage), "api"),
94		ModelsPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ModelPackage), "definitions"),
95		ServerPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ServerPackage), "server"),
96		ClientPackage:   opts.LanguageOpts.MangleName(swag.ToFileName(opts.ClientPackage), "client"),
97		Principal:       opts.Principal,
98		DefaultScheme:   defaultScheme,
99		DefaultProduces: defaultProduces,
100		DefaultConsumes: defaultConsumes,
101		GenOpts:         opts,
102	}
103	generator.Receiver = "o"
104
105	return (&clientGenerator{generator}).Generate()
106}
107
108type clientGenerator struct {
109	appGenerator
110}
111
112func (c *clientGenerator) Generate() error {
113	app, err := c.makeCodegenApp()
114	if app.Name == "" {
115		app.Name = "APIClient"
116	}
117	app.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(c.Target), c.ModelsPackage))}
118	if err != nil {
119		return err
120	}
121
122	if c.DumpData {
123		bb, _ := json.MarshalIndent(swag.ToDynamicJSON(app), "", "  ")
124		fmt.Fprintln(os.Stdout, string(bb))
125		return nil
126	}
127
128	// errChan := make(chan error, 100)
129	// wg := nsync.NewControlWaitGroup(20)
130
131	if c.GenOpts.IncludeModel {
132		for _, mod := range app.Models {
133			// if len(errChan) > 0 {
134			// 	wg.Wait()
135			// 	return <-errChan
136			// }
137			modCopy := mod
138			// wg.Do(func() {
139			modCopy.IncludeValidator = true
140			if err := c.GenOpts.renderDefinition(&modCopy); err != nil {
141				return err
142			}
143			// })
144		}
145	}
146
147	// wg.Wait()
148	if c.GenOpts.IncludeHandler {
149		sort.Sort(app.OperationGroups)
150		for i := range app.OperationGroups {
151			opGroup := app.OperationGroups[i]
152			opGroup.DefaultImports = []string{filepath.ToSlash(filepath.Join(baseImport(c.Target), c.ModelsPackage))}
153			opGroup.RootPackage = c.ClientPackage
154			app.OperationGroups[i] = opGroup
155			sort.Sort(opGroup.Operations)
156			for _, op := range opGroup.Operations {
157				// if len(errChan) > 0 {
158				// 	wg.Wait()
159				// 	return <-errChan
160				// }
161				opCopy := op
162				if opCopy.Package == "" {
163					opCopy.Package = c.Package
164				}
165				// wg.Do(func() {
166				if err := c.GenOpts.renderOperation(&opCopy); err != nil {
167					return err
168				}
169				// })
170			}
171			app.DefaultImports = append(app.DefaultImports, filepath.ToSlash(filepath.Join(baseImport(c.Target), c.ClientPackage, opGroup.Name)))
172
173			// wg.Do(func() {
174			if err := c.GenOpts.renderOperationGroup(&opGroup); err != nil {
175				// errChan <- err
176				return err
177			}
178			// })
179		}
180		// wg.Wait()
181	}
182
183	if c.GenOpts.IncludeSupport {
184		// wg.Do(func() {
185		if err := c.GenOpts.renderApplication(&app); err != nil {
186			return err
187		}
188		// })
189	}
190
191	// wg.Wait()
192
193	// if len(errChan) > 0 {
194	// 	return <-errChan
195	// }
196
197	return nil
198}
199