2019-09-04 21:53:54 +02:00
package commands
import (
"errors"
"io"
"log"
"os"
"github.com/go-openapi/analysis"
"github.com/go-openapi/loads"
"github.com/go-openapi/spec"
flags "github.com/jessevdk/go-flags"
2019-12-18 16:05:30 +01:00
"github.com/go-swagger/go-swagger/generator"
2019-09-04 21:53:54 +02:00
)
const (
// Output messages
nothingToDo = "Nothing to do. Need some swagger files to merge.\nUSAGE: swagger mixin [-c <expected#Collisions>] <primary-swagger-file> <mixin-swagger-file>..."
)
// MixinSpec holds command line flag definitions specific to the mixin
// command. The flags are defined using struct field tags with the
// "github.com/jessevdk/go-flags" format.
type MixinSpec struct {
ExpectedCollisionCount uint ` short:"c" description:"expected # of rejected mixin paths, defs, etc due to existing key. Non-zero exit if does not match actual." `
Compact bool ` long:"compact" description:"applies to JSON formatted specs. When present, doesn't prettify the json" `
Output flags . Filename ` long:"output" short:"o" description:"the file to write to" `
2019-12-18 16:05:30 +01:00
KeepSpecOrder bool ` long:"keep-spec-order" description:"Keep schema properties order identical to spec file" `
2019-09-04 21:53:54 +02:00
Format string ` long:"format" description:"the format for the spec document" default:"json" choice:"yaml" choice:"json" `
}
// Execute runs the mixin command which merges Swagger 2.0 specs into
// one spec
//
// Use cases include adding independently versioned metadata APIs to
// application APIs for microservices.
//
// Typically, multiple APIs to the same service instance is not a
// problem for client generation as you can create more than one
// client to the service from the same calling process (one for each
// API). However, merging clients can improve clarity of client code
// by having a single client to given service vs several.
//
// Server skeleton generation, ie generating the model & marshaling
// code, http server instance etc. from Swagger, becomes easier with a
// merged spec for some tools & target-languages. Server code
// generation tools that natively support hosting multiple specs in
// one server process will not need this tool.
func ( c * MixinSpec ) Execute ( args [ ] string ) error {
if len ( args ) < 2 {
return errors . New ( nothingToDo )
}
log . Printf ( "args[0] = %v\n" , args [ 0 ] )
log . Printf ( "args[1:] = %v\n" , args [ 1 : ] )
collisions , err := c . MixinFiles ( args [ 0 ] , args [ 1 : ] , os . Stdout )
for _ , warn := range collisions {
log . Println ( warn )
}
if err != nil {
return err
}
if len ( collisions ) != int ( c . ExpectedCollisionCount ) {
if len ( collisions ) != 0 {
// use bash $? to get actual # collisions
// (but has to be non-zero)
os . Exit ( len ( collisions ) )
}
os . Exit ( 254 )
}
return nil
}
// MixinFiles is a convenience function for Mixin that reads the given
// swagger files, adds the mixins to primary, calls
// FixEmptyResponseDescriptions on the primary, and writes the primary
// with mixins to the given writer in JSON. Returns the warning
// messages for collisions that occurred during mixin process and any
// error.
func ( c * MixinSpec ) MixinFiles ( primaryFile string , mixinFiles [ ] string , w io . Writer ) ( [ ] string , error ) {
primaryDoc , err := loads . Spec ( primaryFile )
if err != nil {
return nil , err
}
primary := primaryDoc . Spec ( )
var mixins [ ] * spec . Swagger
for _ , mixinFile := range mixinFiles {
2019-12-18 16:05:30 +01:00
if c . KeepSpecOrder {
mixinFile = generator . WithAutoXOrder ( mixinFile )
}
2019-09-04 21:53:54 +02:00
mixin , lerr := loads . Spec ( mixinFile )
if lerr != nil {
return nil , lerr
}
mixins = append ( mixins , mixin . Spec ( ) )
}
collisions := analysis . Mixin ( primary , mixins ... )
analysis . FixEmptyResponseDescriptions ( primary )
return collisions , writeToFile ( primary , ! c . Compact , c . Format , string ( c . Output ) )
}