github.com/circl-dev/go-swagger@v0.31.0/cmd/swagger/commands/generate/shared.go (about)

     1  package generate
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"log"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/circl-dev/analysis"
    12  	"github.com/circl-dev/go-swagger/generator"
    13  	"github.com/go-openapi/swag"
    14  	flags "github.com/jessevdk/go-flags"
    15  	"github.com/spf13/viper"
    16  )
    17  
    18  // FlattenCmdOptions determines options to the flatten spec preprocessing
    19  type FlattenCmdOptions struct {
    20  	WithExpand  bool     `long:"with-expand" description:"expands all $ref's in spec prior to generation (shorthand to --with-flatten=expand)"  group:"shared"`
    21  	WithFlatten []string `long:"with-flatten" description:"flattens all $ref's in spec prior to generation" choice:"minimal" choice:"full" choice:"expand" choice:"verbose" choice:"noverbose" choice:"remove-unused" default:"minimal" default:"verbose" group:"shared"` // nolint: staticcheck
    22  }
    23  
    24  // SetFlattenOptions builds flatten options from command line args
    25  func (f *FlattenCmdOptions) SetFlattenOptions(dflt *analysis.FlattenOpts) (res *analysis.FlattenOpts) {
    26  	res = &analysis.FlattenOpts{}
    27  	if dflt != nil {
    28  		*res = *dflt
    29  	}
    30  	if f == nil {
    31  		return
    32  	}
    33  	verboseIsSet := false
    34  	minimalIsSet := false
    35  	expandIsSet := false
    36  	if f.WithExpand {
    37  		res.Expand = true
    38  		expandIsSet = true
    39  	}
    40  	for _, opt := range f.WithFlatten {
    41  		switch opt {
    42  		case "verbose":
    43  			res.Verbose = true
    44  			verboseIsSet = true
    45  		case "noverbose":
    46  			if !verboseIsSet {
    47  				// verbose flag takes precedence
    48  				res.Verbose = false
    49  				verboseIsSet = true
    50  			}
    51  		case "remove-unused":
    52  			res.RemoveUnused = true
    53  		case "expand":
    54  			res.Expand = true
    55  			expandIsSet = true
    56  		case "full":
    57  			if !minimalIsSet && !expandIsSet {
    58  				// minimal flag takes precedence
    59  				res.Minimal = false
    60  				minimalIsSet = true
    61  			}
    62  		case "minimal":
    63  			if !expandIsSet {
    64  				// expand flag takes precedence
    65  				res.Minimal = true
    66  				minimalIsSet = true
    67  			}
    68  		}
    69  	}
    70  	return
    71  }
    72  
    73  type sharedCommand interface {
    74  	apply(*generator.GenOpts)
    75  	getConfigFile() string
    76  	generate(*generator.GenOpts) error
    77  	log(string)
    78  }
    79  
    80  type schemeOptions struct {
    81  	Principal     string `short:"P" long:"principal" description:"the model to use for the security principal"`
    82  	DefaultScheme string `long:"default-scheme" description:"the default scheme for this API" default:"http"`
    83  
    84  	PrincipalIface bool `long:"principal-is-interface" description:"the security principal provided is an interface, not a struct"`
    85  }
    86  
    87  func (so schemeOptions) apply(opts *generator.GenOpts) {
    88  	opts.Principal = so.Principal
    89  	opts.PrincipalCustomIface = so.PrincipalIface
    90  	opts.DefaultScheme = so.DefaultScheme
    91  }
    92  
    93  type mediaOptions struct {
    94  	DefaultProduces string `long:"default-produces" description:"the default mime type that API operations produce" default:"application/json"`
    95  	DefaultConsumes string `long:"default-consumes" description:"the default mime type that API operations consume" default:"application/json"`
    96  }
    97  
    98  func (m mediaOptions) apply(opts *generator.GenOpts) {
    99  	opts.DefaultProduces = m.DefaultProduces
   100  	opts.DefaultConsumes = m.DefaultConsumes
   101  
   102  	const xmlIdentifier = "xml"
   103  	opts.WithXML = strings.Contains(opts.DefaultProduces, xmlIdentifier) || strings.Contains(opts.DefaultConsumes, xmlIdentifier)
   104  }
   105  
   106  // WithShared adds the shared options group
   107  type WithShared struct {
   108  	Shared sharedOptions `group:"Options common to all code generation commands"`
   109  }
   110  
   111  func (w WithShared) getConfigFile() string {
   112  	return string(w.Shared.ConfigFile)
   113  }
   114  
   115  type sharedOptions struct {
   116  	Spec                  flags.Filename `long:"spec" short:"f" description:"the spec file to use (default swagger.{json,yml,yaml})" group:"shared"`
   117  	Target                flags.Filename `long:"target" short:"t" default:"./" description:"the base directory for generating the files" group:"shared"`
   118  	Template              string         `long:"template" description:"load contributed templates" choice:"stratoscale" group:"shared"`
   119  	TemplateDir           flags.Filename `long:"template-dir" short:"T" description:"alternative template override directory" group:"shared"`
   120  	ConfigFile            flags.Filename `long:"config-file" short:"C" description:"configuration file to use for overriding template options" group:"shared"`
   121  	CopyrightFile         flags.Filename `long:"copyright-file" short:"r" description:"copyright file used to add copyright header" group:"shared"`
   122  	AdditionalInitialisms []string       `long:"additional-initialism" description:"consecutive capitals that should be considered intialisms" group:"shared"`
   123  	AllowTemplateOverride bool           `long:"allow-template-override" description:"allows overriding protected templates" group:"shared"`
   124  	SkipValidation        bool           `long:"skip-validation" description:"skips validation of spec prior to generation" group:"shared"`
   125  	DumpData              bool           `long:"dump-data" description:"when present dumps the json for the template generator instead of generating files" group:"shared"`
   126  	StrictResponders      bool           `long:"strict-responders" description:"Use strict type for the handler return value"`
   127  	FlattenCmdOptions
   128  }
   129  
   130  func (s sharedOptions) apply(opts *generator.GenOpts) {
   131  	opts.Spec = string(s.Spec)
   132  	opts.Target = string(s.Target)
   133  	opts.Template = s.Template
   134  	opts.TemplateDir = string(s.TemplateDir)
   135  	opts.AllowTemplateOverride = s.AllowTemplateOverride
   136  	opts.ValidateSpec = !s.SkipValidation
   137  	opts.DumpData = s.DumpData
   138  	opts.FlattenOpts = s.FlattenCmdOptions.SetFlattenOptions(opts.FlattenOpts)
   139  	opts.Copyright = string(s.CopyrightFile)
   140  	opts.StrictResponders = s.StrictResponders
   141  
   142  	swag.AddInitialisms(s.AdditionalInitialisms...)
   143  }
   144  
   145  func setCopyright(copyrightFile string) (string, error) {
   146  	// read the Copyright from file path in opts
   147  	if copyrightFile == "" {
   148  		return "", nil
   149  	}
   150  	bytebuffer, err := ioutil.ReadFile(copyrightFile)
   151  	if err != nil {
   152  		return "", err
   153  	}
   154  	return string(bytebuffer), nil
   155  }
   156  
   157  func createSwagger(s sharedCommand) error {
   158  	cfg, err := readConfig(s.getConfigFile())
   159  	if err != nil {
   160  		return err
   161  	}
   162  	setDebug(cfg) // viper config Debug
   163  
   164  	opts := new(generator.GenOpts)
   165  	s.apply(opts)
   166  
   167  	opts.Copyright, err = setCopyright(opts.Copyright)
   168  	if err != nil {
   169  		return fmt.Errorf("could not load copyright file: %v", err)
   170  	}
   171  
   172  	if opts.Template != "" {
   173  		contribOptionsOverride(opts)
   174  	}
   175  
   176  	if err = opts.EnsureDefaults(); err != nil {
   177  		return err
   178  	}
   179  
   180  	if err = configureOptsFromConfig(cfg, opts); err != nil {
   181  		return err
   182  	}
   183  
   184  	if err = s.generate(opts); err != nil {
   185  		return err
   186  	}
   187  
   188  	basepath, err := filepath.Abs(".")
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	targetAbs, err := filepath.Abs(opts.Target)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	rp, err := filepath.Rel(basepath, targetAbs)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	s.log(rp)
   203  
   204  	return nil
   205  }
   206  
   207  func readConfig(filename string) (*viper.Viper, error) {
   208  	if filename == "" {
   209  		return nil, nil
   210  	}
   211  
   212  	abspath, err := filepath.Abs(filename)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	log.Println("trying to read config from", abspath)
   217  	return generator.ReadConfig(abspath)
   218  }
   219  
   220  func configureOptsFromConfig(cfg *viper.Viper, opts *generator.GenOpts) error {
   221  	if cfg == nil {
   222  		return nil
   223  	}
   224  
   225  	var def generator.LanguageDefinition
   226  	if err := cfg.Unmarshal(&def); err != nil {
   227  		return err
   228  	}
   229  	return def.ConfigureOpts(opts)
   230  }
   231  
   232  func setDebug(cfg *viper.Viper) {
   233  	// viper config debug
   234  	if os.Getenv("DEBUG") != "" || os.Getenv("SWAGGER_DEBUG") != "" {
   235  		if cfg != nil {
   236  			cfg.Debug()
   237  		} else {
   238  			log.Println("No config read")
   239  		}
   240  	}
   241  }