github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/cmd/swagger/commands/mixin.go (about) 1 package commands 2 3 import ( 4 "errors" 5 "io" 6 "log" 7 "os" 8 9 "github.com/go-openapi/analysis" 10 "github.com/go-openapi/loads" 11 "github.com/go-openapi/spec" 12 flags "github.com/jessevdk/go-flags" 13 14 "github.com/thetreep/go-swagger/generator" 15 ) 16 17 const ( 18 // Output messages 19 nothingToDo = "nothing to do. Need some swagger files to merge.\nUSAGE: swagger mixin [-c <expected#Collisions>] <primary-swagger-file> <mixin-swagger-file...>" 20 ignoreConflictsAndCollisionsSpecified = "both the flags ignore conflicts and collisions were specified. These have conflicting meaning so please only specify one" 21 ) 22 23 // MixinSpec holds command line flag definitions specific to the mixin 24 // command. The flags are defined using struct field tags with the 25 // "github.com/jessevdk/go-flags" format. 26 type MixinSpec struct { 27 ExpectedCollisionCount uint `short:"c" description:"expected # of rejected mixin paths, defs, etc due to existing key. Non-zero exit if does not match actual."` 28 Compact bool `long:"compact" description:"applies to JSON formatted specs. When present, doesn't prettify the json"` 29 Output flags.Filename `long:"output" short:"o" description:"the file to write to"` 30 KeepSpecOrder bool `long:"keep-spec-order" description:"Keep schema properties order identical to spec file"` 31 Format string `long:"format" description:"the format for the spec document" default:"json" choice:"yaml" choice:"json"` 32 IgnoreConflicts bool `long:"ignore-conflicts" description:"Ignore conflict"` 33 } 34 35 // Execute runs the mixin command which merges Swagger 2.0 specs into 36 // one spec 37 // 38 // Use cases include adding independently versioned metadata APIs to 39 // application APIs for microservices. 40 // 41 // Typically, multiple APIs to the same service instance is not a 42 // problem for client generation as you can create more than one 43 // client to the service from the same calling process (one for each 44 // API). However, merging clients can improve clarity of client code 45 // by having a single client to given service vs several. 46 // 47 // Server skeleton generation, ie generating the model & marshaling 48 // code, http server instance etc. from Swagger, becomes easier with a 49 // merged spec for some tools & target-languages. Server code 50 // generation tools that natively support hosting multiple specs in 51 // one server process will not need this tool. 52 func (c *MixinSpec) Execute(args []string) error { 53 54 if len(args) < 2 { 55 return errors.New(nothingToDo) 56 } 57 if c.IgnoreConflicts && c.ExpectedCollisionCount != 0 { 58 return errors.New(ignoreConflictsAndCollisionsSpecified) 59 } 60 61 log.Printf("args[0] = %v\n", args[0]) 62 log.Printf("args[1:] = %v\n", args[1:]) 63 collisions, err := c.MixinFiles(args[0], args[1:], os.Stdout) 64 65 for _, warn := range collisions { 66 log.Println(warn) 67 } 68 69 if err != nil { 70 return err 71 } 72 73 if c.IgnoreConflicts { 74 return nil 75 } 76 if len(collisions) != int(c.ExpectedCollisionCount) { 77 if len(collisions) != 0 { 78 // use bash $? to get actual # collisions 79 // (but has to be non-zero) 80 os.Exit(len(collisions)) 81 } 82 os.Exit(254) 83 } 84 return nil 85 } 86 87 // MixinFiles is a convenience function for Mixin that reads the given 88 // swagger files, adds the mixins to primary, calls 89 // FixEmptyResponseDescriptions on the primary, and writes the primary 90 // with mixins to the given writer in JSON. Returns the warning 91 // messages for collisions that occurred during mixin process and any 92 // error. 93 func (c *MixinSpec) MixinFiles(primaryFile string, mixinFiles []string, w io.Writer) ([]string, error) { 94 95 primaryDoc, err := loads.Spec(primaryFile) 96 if err != nil { 97 return nil, err 98 } 99 primary := primaryDoc.Spec() 100 101 var mixins []*spec.Swagger 102 for _, mixinFile := range mixinFiles { 103 if c.KeepSpecOrder { 104 mixinFile = generator.WithAutoXOrder(mixinFile) 105 } 106 mixin, lerr := loads.Spec(mixinFile) 107 if lerr != nil { 108 return nil, lerr 109 } 110 mixins = append(mixins, mixin.Spec()) 111 } 112 113 collisions := analysis.Mixin(primary, mixins...) 114 analysis.FixEmptyResponseDescriptions(primary) 115 116 return collisions, writeToFile(primary, !c.Compact, c.Format, string(c.Output)) 117 }