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  }