/
mixin.go
117 lines (101 loc) · 4.1 KB
/
mixin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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"
"github.com/go-swagger/go-swagger/generator"
)
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...>"
ignoreConflictsAndCollisionsSpecified = "both the flags ignore conflicts and collisions were specified. These have conflicting meaning so please only specify one"
)
// 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"`
KeepSpecOrder bool `long:"keep-spec-order" description:"Keep schema properties order identical to spec file"`
Format string `long:"format" description:"the format for the spec document" default:"json" choice:"yaml" choice:"json"`
IgnoreConflicts bool `long:"ignore-conflicts" description:"Ignore conflict"`
}
// 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)
}
if c.IgnoreConflicts && c.ExpectedCollisionCount != 0 {
return errors.New(ignoreConflictsAndCollisionsSpecified)
}
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 c.IgnoreConflicts {
return nil
}
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 {
if c.KeepSpecOrder {
mixinFile = generator.WithAutoXOrder(mixinFile)
}
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))
}