github.com/josephspurrier/go-swagger@v0.2.1-0.20221129144919-1f672a142a00/cmd/swagger/commands/generate/shared.go (about)

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