github.com/Tyktechnologies/tyk@v2.9.5+incompatible/cli/importer/importer.go (about) 1 package importer 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "strings" 9 10 log "github.com/sirupsen/logrus" 11 12 kingpin "gopkg.in/alecthomas/kingpin.v2" 13 14 "github.com/TykTechnologies/tyk/apidef" 15 "github.com/TykTechnologies/tyk/apidef/importer" 16 ) 17 18 const ( 19 cmdName = "import" 20 cmdDesc = "Imports a BluePrint/Swagger/WSDL file" 21 ) 22 23 var ( 24 imp *Importer 25 errUnknownMode = errors.New("Unknown mode") 26 ) 27 28 // Importer wraps the import functionality. 29 type Importer struct { 30 input *string 31 swaggerMode *bool 32 bluePrintMode *bool 33 wsdlMode *bool 34 portNames *string 35 createAPI *bool 36 orgID *string 37 upstreamTarget *string 38 asMock *bool 39 forAPI *string 40 asVersion *string 41 } 42 43 func init() { 44 imp = &Importer{} 45 } 46 47 // AddTo initializes an importer object. 48 func AddTo(app *kingpin.Application) { 49 cmd := app.Command(cmdName, cmdDesc) 50 imp.input = cmd.Arg("input file", "e.g. blueprint.json, swagger.json, service.wsdl etc.").String() 51 imp.swaggerMode = cmd.Flag("swagger", "Use Swagger mode").Bool() 52 imp.bluePrintMode = cmd.Flag("blueprint", "Use BluePrint mode").Bool() 53 imp.wsdlMode = cmd.Flag("wsdl", "Use WSDL mode").Bool() 54 imp.portNames = cmd.Flag("port-names", "Specify port name of each service in the WSDL file. Input format is comma separated list of serviceName:portName").String() 55 imp.createAPI = cmd.Flag("create-api", "Creates a new API definition from the blueprint").Bool() 56 imp.orgID = cmd.Flag("org-id", "assign the API Definition to this org_id (required with create-api").String() 57 imp.upstreamTarget = cmd.Flag("upstream-target", "set the upstream target for the definition").PlaceHolder("URL").String() 58 imp.asMock = cmd.Flag("as-mock", "creates the API as a mock based on example fields").Bool() 59 imp.forAPI = cmd.Flag("for-api", "adds blueprint to existing API Definition as version").PlaceHolder("PATH").String() 60 imp.asVersion = cmd.Flag("as-version", "the version number to use when inserting").PlaceHolder("VERSION").String() 61 cmd.Action(imp.Import) 62 } 63 64 // Import performs the import process. 65 func (i *Importer) Import(ctx *kingpin.ParseContext) (err error) { 66 if *i.swaggerMode { 67 err = i.handleSwaggerMode() 68 if err != nil { 69 log.Fatal(err) 70 os.Exit(1) 71 } 72 } else if *i.bluePrintMode { 73 err = i.handleBluePrintMode() 74 if err != nil { 75 log.Fatal(err) 76 os.Exit(1) 77 } 78 } else if *i.wsdlMode { 79 err = i.handleWSDLMode() 80 if err != nil { 81 log.Fatal(err) 82 os.Exit(1) 83 } 84 } else { 85 log.Fatal(errUnknownMode) 86 os.Exit(1) 87 } 88 os.Exit(0) 89 return nil 90 } 91 92 func (i *Importer) validateInput() error { 93 94 if *i.createAPI { 95 if *i.upstreamTarget == "" || *i.orgID == "" { 96 return fmt.Errorf("No upstream target or org ID defined, these are both required") 97 } 98 } else { 99 if *i.forAPI == "" { 100 return fmt.Errorf("If adding to an API, the path to the definition must be listed") 101 } 102 103 if *i.asVersion == "" { 104 return fmt.Errorf("No version defined for this import operation, please set an import ID using the --as-version flag") 105 } 106 } 107 108 return nil 109 } 110 111 func (i *Importer) processPortNames() map[string]string { 112 p := make(map[string]string) 113 114 if *i.portNames == "" { 115 return p 116 } 117 118 pairs := strings.Split(*i.portNames, ",") 119 120 for _, v := range pairs { 121 components := strings.Split(v, ":") 122 p[components[0]] = components[1] 123 } 124 125 return p 126 } 127 128 func (i *Importer) handleBluePrintMode() error { 129 if !*i.createAPI { 130 // Different branch, here we need an API Definition to modify 131 if *i.forAPI == "" { 132 return fmt.Errorf("If adding to an API, the path to the definition must be listed") 133 } 134 135 if *i.asVersion == "" { 136 return fmt.Errorf("No version defined for this import operation, please set an import ID using the --as-version flag") 137 } 138 139 defFromFile, err := i.apiDefLoadFile(*i.forAPI) 140 if err != nil { 141 return fmt.Errorf("failed to load and decode file data for API Definition: %v", err) 142 } 143 144 bp, err := i.bluePrintLoadFile(*i.input) 145 if err != nil { 146 return fmt.Errorf("File load error: %v", err) 147 } 148 versionData, err := bp.ConvertIntoApiVersion(*i.asMock) 149 if err != nil { 150 return fmt.Errorf("onversion into API Def failed: %v", err) 151 } 152 153 if err := bp.InsertIntoAPIDefinitionAsVersion(versionData, defFromFile, *i.asVersion); err != nil { 154 return fmt.Errorf("Insertion failed: %v", err) 155 } 156 157 i.printDef(defFromFile) 158 159 } 160 161 if *i.upstreamTarget == "" && *i.orgID == "" { 162 return fmt.Errorf("No upstream target or org ID defined, these are both required") 163 } 164 165 // Create the API with the blueprint 166 bp, err := i.bluePrintLoadFile(*i.input) 167 if err != nil { 168 return fmt.Errorf("File load error: %v", err) 169 } 170 171 def, err := bp.ToAPIDefinition(*i.orgID, *i.upstreamTarget, *i.asMock) 172 if err != nil { 173 return fmt.Errorf("Failed to create API Definition from file") 174 } 175 176 i.printDef(def) 177 return nil 178 } 179 180 func (i *Importer) handleSwaggerMode() error { 181 if *i.createAPI { 182 if *i.upstreamTarget != "" && *i.orgID != "" { 183 // Create the API with the blueprint 184 s, err := i.swaggerLoadFile(*i.input) 185 if err != nil { 186 return fmt.Errorf("File load error: %v", err) 187 } 188 189 def, err := s.ToAPIDefinition(*i.orgID, *i.upstreamTarget, *i.asMock) 190 if err != nil { 191 return fmt.Errorf("Failed to create API Defintition from file") 192 } 193 194 i.printDef(def) 195 return nil 196 } 197 198 return fmt.Errorf("No upstream target or org ID defined, these are both required") 199 200 } 201 202 // Different branch, here we need an API Definition to modify 203 if *i.forAPI == "" { 204 return fmt.Errorf("If adding to an API, the path to the definition must be listed") 205 } 206 207 if *i.asVersion == "" { 208 return fmt.Errorf("No version defined for this import operation, please set an import ID using the --as-version flag") 209 } 210 211 defFromFile, err := i.apiDefLoadFile(*i.forAPI) 212 if err != nil { 213 return fmt.Errorf("failed to load and decode file data for API Definition: %v", err) 214 } 215 216 s, err := i.swaggerLoadFile(*i.input) 217 if err != nil { 218 return fmt.Errorf("File load error: %v", err) 219 } 220 221 versionData, err := s.ConvertIntoApiVersion(*i.asMock) 222 if err != nil { 223 return fmt.Errorf("Conversion into API Def failed: %v", err) 224 } 225 226 if err := s.InsertIntoAPIDefinitionAsVersion(versionData, defFromFile, *i.asVersion); err != nil { 227 return fmt.Errorf("Insertion failed: %v", err) 228 } 229 230 i.printDef(defFromFile) 231 232 return nil 233 } 234 235 func (i *Importer) handleWSDLMode() error { 236 var def *apidef.APIDefinition 237 238 //Process Input 239 if err := i.validateInput(); err != nil { 240 return err 241 } 242 serviceportMapping := i.processPortNames() 243 244 //Load WSDL file 245 w, err := i.wsdlLoadFile(*i.input) 246 if err != nil { 247 return fmt.Errorf("File load error: %v", err) 248 } 249 250 w.SetServicePortMapping(serviceportMapping) 251 252 if *i.createAPI { 253 //Create new API 254 def, err = w.ToAPIDefinition(*i.orgID, *i.upstreamTarget, *i.asMock) 255 if err != nil { 256 return fmt.Errorf("Failed to create API Defintition from file") 257 } 258 } else { 259 //Add into existing API 260 def, err = i.apiDefLoadFile(*i.forAPI) 261 if err != nil { 262 return fmt.Errorf("failed to load and decode file data for API Definition: %v", err) 263 } 264 265 versionData, err := w.ConvertIntoApiVersion(*i.asMock) 266 if err != nil { 267 return fmt.Errorf("Conversion into API Def failed: %v", err) 268 } 269 270 if err := w.InsertIntoAPIDefinitionAsVersion(versionData, def, *i.asVersion); err != nil { 271 return fmt.Errorf("Insertion failed: %v", err) 272 } 273 274 } 275 276 i.printDef(def) 277 278 return nil 279 } 280 281 func (i *Importer) printDef(def *apidef.APIDefinition) { 282 asJSON, err := json.MarshalIndent(def, "", " ") 283 if err != nil { 284 log.Error("Marshalling failed: ", err) 285 } 286 287 // The id attribute is for BSON only and breaks the parser if it's empty, cull it here. 288 fixed := strings.Replace(string(asJSON), ` "id": "",`, "", 1) 289 fmt.Println(fixed) 290 } 291 292 func (i *Importer) swaggerLoadFile(path string) (*importer.SwaggerAST, error) { 293 swagger, err := importer.GetImporterForSource(importer.SwaggerSource) 294 if err != nil { 295 return nil, err 296 } 297 f, err := os.Open(path) 298 if err != nil { 299 return nil, err 300 } 301 defer f.Close() 302 303 if err := swagger.LoadFrom(f); err != nil { 304 return nil, err 305 } 306 307 return swagger.(*importer.SwaggerAST), nil 308 } 309 310 func (i *Importer) wsdlLoadFile(path string) (*importer.WSDLDef, error) { 311 wsdl, err := importer.GetImporterForSource(importer.WSDLSource) 312 if err != nil { 313 return nil, err 314 } 315 f, err := os.Open(path) 316 if err != nil { 317 return nil, err 318 } 319 defer f.Close() 320 321 if err := wsdl.LoadFrom(f); err != nil { 322 return nil, err 323 } 324 325 return wsdl.(*importer.WSDLDef), nil 326 } 327 328 func (i *Importer) bluePrintLoadFile(path string) (*importer.BluePrintAST, error) { 329 blueprint, err := importer.GetImporterForSource(importer.ApiaryBluePrint) 330 if err != nil { 331 return nil, err 332 } 333 334 f, err := os.Open(path) 335 if err != nil { 336 return nil, err 337 } 338 defer f.Close() 339 340 if err := blueprint.LoadFrom(f); err != nil { 341 return nil, err 342 } 343 344 return blueprint.(*importer.BluePrintAST), nil 345 } 346 347 func (i *Importer) apiDefLoadFile(path string) (*apidef.APIDefinition, error) { 348 f, err := os.Open(path) 349 if err != nil { 350 return nil, err 351 } 352 def := &apidef.APIDefinition{} 353 if err := json.NewDecoder(f).Decode(&def); err != nil { 354 return nil, err 355 } 356 return def, nil 357 }