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 }