github.com/aavshr/aws-sdk-go@v1.41.3/private/model/cli/gen-api/main.go (about)

     1  //go:build codegen
     2  // +build codegen
     3  
     4  // Command aws-gen-gocli parses a JSON description of an AWS API and generates a
     5  // Go file containing a client for the API.
     6  //
     7  //     aws-gen-gocli apis/s3/2006-03-03/api-2.json
     8  package main
     9  
    10  import (
    11  	"flag"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"os"
    15  	"path/filepath"
    16  	"runtime/debug"
    17  	"strings"
    18  	"sync"
    19  
    20  	"github.com/aavshr/aws-sdk-go/private/model/api"
    21  	"github.com/aavshr/aws-sdk-go/private/util"
    22  )
    23  
    24  func usage() {
    25  	fmt.Fprintln(os.Stderr, `Usage: api-gen <options> [model path | file path]
    26  Loads API models from file and generates SDK clients from the models.
    27  
    28  The model path arguments can be globs, or paths to individual files. The
    29  utiliity requires that the API model files follow the following pattern:
    30  
    31  <root>/<servicename>/<api-version>/<model json files>
    32  
    33  e.g:
    34  
    35  ./models/apis/s3/2006-03-01/*.json
    36  
    37  Flags:`)
    38  	flag.PrintDefaults()
    39  }
    40  
    41  // Generates service api, examples, and interface from api json definition files.
    42  //
    43  // Flags:
    44  // -path alternative service path to write generated files to for each service.
    45  //
    46  // Env:
    47  //  SERVICES comma separated list of services to generate.
    48  func main() {
    49  	var svcPath, svcImportPath string
    50  	flag.StringVar(&svcPath, "path", "service",
    51  		"The `path` to generate service clients in to.",
    52  	)
    53  	flag.StringVar(&svcImportPath, "svc-import-path",
    54  		api.SDKImportRoot+"/service",
    55  		"The Go `import path` to generate client to be under.",
    56  	)
    57  	var ignoreUnsupportedAPIs bool
    58  	flag.BoolVar(&ignoreUnsupportedAPIs, "ignore-unsupported-apis",
    59  		true,
    60  		"Ignores API models that use unsupported features",
    61  	)
    62  	flag.Usage = usage
    63  	flag.Parse()
    64  
    65  	if len(os.Getenv("AWS_SDK_CODEGEN_DEBUG")) != 0 {
    66  		api.LogDebug(os.Stdout)
    67  	}
    68  
    69  	// Make sure all paths are based on platform's pathing not Unix
    70  	globs := flag.Args()
    71  	for i, g := range globs {
    72  		globs[i] = filepath.FromSlash(g)
    73  	}
    74  	svcPath = filepath.FromSlash(svcPath)
    75  
    76  	modelPaths, err := api.ExpandModelGlobPath(globs...)
    77  	if err != nil {
    78  		fmt.Fprintln(os.Stderr, "failed to glob file pattern", err)
    79  		os.Exit(1)
    80  	}
    81  	modelPaths, _ = api.TrimModelServiceVersions(modelPaths)
    82  
    83  	loader := api.Loader{
    84  		BaseImport:            svcImportPath,
    85  		IgnoreUnsupportedAPIs: ignoreUnsupportedAPIs,
    86  	}
    87  
    88  	apis, err := loader.Load(modelPaths)
    89  	if err != nil {
    90  		fmt.Fprintln(os.Stderr, "failed to load API models", err)
    91  		os.Exit(1)
    92  	}
    93  	if len(apis) == 0 {
    94  		fmt.Fprintf(os.Stderr, "expected to load models, but found none")
    95  		os.Exit(1)
    96  	}
    97  
    98  	if v := os.Getenv("SERVICES"); len(v) != 0 {
    99  		svcs := strings.Split(v, ",")
   100  		for pkgName, a := range apis {
   101  			var found bool
   102  			for _, include := range svcs {
   103  				if a.PackageName() == include {
   104  					found = true
   105  					break
   106  				}
   107  			}
   108  			if !found {
   109  				delete(apis, pkgName)
   110  			}
   111  		}
   112  	}
   113  
   114  	var wg sync.WaitGroup
   115  	servicePaths := map[string]struct{}{}
   116  	for _, a := range apis {
   117  		if _, ok := excludeServices[a.PackageName()]; ok {
   118  			continue
   119  		}
   120  
   121  		// Create the output path for the model.
   122  		pkgDir := filepath.Join(svcPath, a.PackageName())
   123  		os.MkdirAll(filepath.Join(pkgDir, a.InterfacePackageName()), 0775)
   124  
   125  		if _, ok := servicePaths[pkgDir]; ok {
   126  			fmt.Fprintf(os.Stderr,
   127  				"attempted to generate a client into %s twice. Second model package, %v\n",
   128  				pkgDir, a.PackageName())
   129  			os.Exit(1)
   130  		}
   131  		servicePaths[pkgDir] = struct{}{}
   132  
   133  		g := &generateInfo{
   134  			API:        a,
   135  			PackageDir: pkgDir,
   136  		}
   137  
   138  		wg.Add(1)
   139  		go func() {
   140  			defer wg.Done()
   141  			writeServiceFiles(g, pkgDir)
   142  		}()
   143  	}
   144  
   145  	wg.Wait()
   146  }
   147  
   148  type generateInfo struct {
   149  	*api.API
   150  	PackageDir string
   151  }
   152  
   153  var excludeServices = map[string]struct{}{
   154  	"importexport": {},
   155  }
   156  
   157  func writeServiceFiles(g *generateInfo, pkgDir string) {
   158  	defer func() {
   159  		if r := recover(); r != nil {
   160  			fmt.Fprintf(os.Stderr, "Error generating %s\n%s\n%s\n",
   161  				pkgDir, r, debug.Stack())
   162  			os.Exit(1)
   163  		}
   164  	}()
   165  
   166  	fmt.Printf("Generating %s (%s)...\n",
   167  		g.API.PackageName(), g.API.Metadata.APIVersion)
   168  
   169  	// write files for service client and API
   170  	Must(writeServiceDocFile(g))
   171  	Must(writeAPIFile(g))
   172  	Must(writeServiceFile(g))
   173  	Must(writeInterfaceFile(g))
   174  	Must(writeWaitersFile(g))
   175  	Must(writeAPIErrorsFile(g))
   176  	Must(writeExamplesFile(g))
   177  
   178  	if g.API.HasEventStream {
   179  		Must(writeAPIEventStreamTestFile(g))
   180  	}
   181  
   182  	if g.API.PackageName() == "s3" {
   183  		Must(writeS3ManagerUploadInputFile(g))
   184  	}
   185  
   186  	if len(g.API.SmokeTests.TestCases) > 0 {
   187  		Must(writeAPISmokeTestsFile(g))
   188  	}
   189  }
   190  
   191  // Must will panic if the error passed in is not nil.
   192  func Must(err error) {
   193  	if err != nil {
   194  		panic(err)
   195  	}
   196  }
   197  
   198  const codeLayout = `// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
   199  
   200  %s
   201  package %s
   202  
   203  %s
   204  `
   205  
   206  func writeGoFile(file string, layout string, args ...interface{}) error {
   207  	return ioutil.WriteFile(file, []byte(util.GoFmt(fmt.Sprintf(layout, args...))), 0664)
   208  }
   209  
   210  // writeServiceDocFile generates the documentation for service package.
   211  func writeServiceDocFile(g *generateInfo) error {
   212  	return writeGoFile(filepath.Join(g.PackageDir, "doc.go"),
   213  		codeLayout,
   214  		strings.TrimSpace(g.API.ServicePackageDoc()),
   215  		g.API.PackageName(),
   216  		"",
   217  	)
   218  }
   219  
   220  // writeExamplesFile writes out the service example file.
   221  func writeExamplesFile(g *generateInfo) error {
   222  	code := g.API.ExamplesGoCode()
   223  	if len(code) > 0 {
   224  		return writeGoFile(filepath.Join(g.PackageDir, "examples_test.go"),
   225  			codeLayout,
   226  			"",
   227  			g.API.PackageName()+"_test",
   228  			code,
   229  		)
   230  	}
   231  	return nil
   232  }
   233  
   234  // writeServiceFile writes out the service initialization file.
   235  func writeServiceFile(g *generateInfo) error {
   236  	return writeGoFile(filepath.Join(g.PackageDir, "service.go"),
   237  		codeLayout,
   238  		"",
   239  		g.API.PackageName(),
   240  		g.API.ServiceGoCode(),
   241  	)
   242  }
   243  
   244  // writeInterfaceFile writes out the service interface file.
   245  func writeInterfaceFile(g *generateInfo) error {
   246  	const pkgDoc = `
   247  // Package %s provides an interface to enable mocking the %s service client
   248  // for testing your code.
   249  //
   250  // It is important to note that this interface will have breaking changes
   251  // when the service model is updated and adds new API operations, paginators,
   252  // and waiters.`
   253  	return writeGoFile(filepath.Join(g.PackageDir, g.API.InterfacePackageName(), "interface.go"),
   254  		codeLayout,
   255  		fmt.Sprintf(pkgDoc, g.API.InterfacePackageName(), g.API.Metadata.ServiceFullName),
   256  		g.API.InterfacePackageName(),
   257  		g.API.InterfaceGoCode(),
   258  	)
   259  }
   260  
   261  func writeWaitersFile(g *generateInfo) error {
   262  	if len(g.API.Waiters) == 0 {
   263  		return nil
   264  	}
   265  
   266  	return writeGoFile(filepath.Join(g.PackageDir, "waiters.go"),
   267  		codeLayout,
   268  		"",
   269  		g.API.PackageName(),
   270  		g.API.WaitersGoCode(),
   271  	)
   272  }
   273  
   274  // writeAPIFile writes out the service API file.
   275  func writeAPIFile(g *generateInfo) error {
   276  	return writeGoFile(filepath.Join(g.PackageDir, "api.go"),
   277  		codeLayout,
   278  		"",
   279  		g.API.PackageName(),
   280  		g.API.APIGoCode(),
   281  	)
   282  }
   283  
   284  // writeAPIErrorsFile writes out the service API errors file.
   285  func writeAPIErrorsFile(g *generateInfo) error {
   286  	return writeGoFile(filepath.Join(g.PackageDir, "errors.go"),
   287  		codeLayout,
   288  		"",
   289  		g.API.PackageName(),
   290  		g.API.APIErrorsGoCode(),
   291  	)
   292  }
   293  
   294  func writeAPIEventStreamTestFile(g *generateInfo) error {
   295  	return writeGoFile(filepath.Join(g.PackageDir, "eventstream_test.go"),
   296  		codeLayout,
   297  		"//go:build go1.16\n// +build go1.16\n",
   298  		g.API.PackageName(),
   299  		g.API.APIEventStreamTestGoCode(),
   300  	)
   301  }
   302  
   303  func writeS3ManagerUploadInputFile(g *generateInfo) error {
   304  	return writeGoFile(filepath.Join(g.PackageDir, "s3manager", "upload_input.go"),
   305  		codeLayout,
   306  		"",
   307  		"s3manager",
   308  		api.S3ManagerUploadInputGoCode(g.API),
   309  	)
   310  }
   311  
   312  func writeAPISmokeTestsFile(g *generateInfo) error {
   313  	return writeGoFile(filepath.Join(g.PackageDir, "integ_test.go"),
   314  		codeLayout,
   315  		"//go:build go1.16 && integration\n// +build go1.16,integration\n",
   316  		g.API.PackageName()+"_test",
   317  		g.API.APISmokeTestsGoCode(),
   318  	)
   319  }