2017-12-08 12:03:10 +00:00
|
|
|
// +build codegen
|
|
|
|
|
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2019-01-21 14:27:20 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
2017-12-08 12:03:10 +00:00
|
|
|
)
|
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
// APIs provides a set of API models loaded by API package name.
|
|
|
|
type APIs map[string]*API
|
|
|
|
|
|
|
|
// LoadAPIs loads the API model files from disk returning the map of API
|
|
|
|
// package. Returns error if multiple API model resolve to the same package
|
|
|
|
// name.
|
|
|
|
func LoadAPIs(modelPaths []string, baseImport string) (APIs, error) {
|
|
|
|
apis := APIs{}
|
|
|
|
for _, modelPath := range modelPaths {
|
|
|
|
a, err := loadAPI(modelPath, baseImport)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to load API, %v, %v", modelPath, err)
|
|
|
|
}
|
|
|
|
importPath := a.ImportPath()
|
|
|
|
if _, ok := apis[importPath]; ok {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"package names must be unique attempted to load %v twice. Second model file: %v",
|
|
|
|
importPath, modelPath)
|
|
|
|
}
|
|
|
|
apis[importPath] = a
|
|
|
|
}
|
|
|
|
|
|
|
|
return apis, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadAPI(modelPath, baseImport string) (*API, error) {
|
|
|
|
a := &API{
|
|
|
|
BaseImportPath: baseImport,
|
|
|
|
BaseCrosslinkURL: "https://docs.aws.amazon.com",
|
|
|
|
}
|
|
|
|
|
|
|
|
modelFile := filepath.Base(modelPath)
|
|
|
|
modelDir := filepath.Dir(modelPath)
|
|
|
|
err := attachModelFiles(modelDir,
|
|
|
|
modelLoader{modelFile, a.Attach, true},
|
|
|
|
modelLoader{"docs-2.json", a.AttachDocs, false},
|
|
|
|
modelLoader{"paginators-1.json", a.AttachPaginators, false},
|
|
|
|
modelLoader{"waiters-2.json", a.AttachWaiters, false},
|
|
|
|
modelLoader{"examples-1.json", a.AttachExamples, false},
|
|
|
|
modelLoader{"smoke.json", a.AttachSmokeTests, false},
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-12-08 12:03:10 +00:00
|
|
|
a.Setup()
|
2019-01-21 14:27:20 +00:00
|
|
|
|
|
|
|
return a, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type modelLoader struct {
|
|
|
|
Filename string
|
|
|
|
Loader func(string)
|
|
|
|
Required bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func attachModelFiles(modelPath string, modelFiles ...modelLoader) error {
|
|
|
|
for _, m := range modelFiles {
|
|
|
|
filepath := filepath.Join(modelPath, m.Filename)
|
|
|
|
_, err := os.Stat(filepath)
|
|
|
|
if os.IsNotExist(err) && !m.Required {
|
|
|
|
continue
|
|
|
|
} else if err != nil {
|
|
|
|
return fmt.Errorf("failed to load model file %v, %v", m.Filename, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
m.Loader(filepath)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExpandModelGlobPath returns a slice of model paths expanded from the glob
|
|
|
|
// pattern passed in. Returns the path of the model file to be loaded. Includes
|
|
|
|
// all versions of a service model.
|
|
|
|
//
|
|
|
|
// e.g:
|
|
|
|
// models/apis/*/*/api-2.json
|
|
|
|
//
|
|
|
|
// Or with specific model file:
|
|
|
|
// models/apis/service/version/api-2.json
|
|
|
|
func ExpandModelGlobPath(globs ...string) ([]string, error) {
|
|
|
|
modelPaths := []string{}
|
|
|
|
|
|
|
|
for _, g := range globs {
|
|
|
|
filepaths, err := filepath.Glob(g)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, p := range filepaths {
|
|
|
|
modelPaths = append(modelPaths, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return modelPaths, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// TrimModelServiceVersions sorts the model paths by service version then
|
|
|
|
// returns recent model versions, and model version excluded.
|
|
|
|
//
|
|
|
|
// Uses the third from last path element to determine unique service. Only one
|
|
|
|
// service version will be included.
|
|
|
|
//
|
|
|
|
// models/apis/service/version/api-2.json
|
|
|
|
func TrimModelServiceVersions(modelPaths []string) (include, exclude []string) {
|
|
|
|
sort.Strings(modelPaths)
|
|
|
|
|
|
|
|
// Remove old API versions from list
|
|
|
|
m := map[string]struct{}{}
|
|
|
|
for i := len(modelPaths) - 1; i >= 0; i-- {
|
|
|
|
// service name is 2nd-to-last component
|
|
|
|
parts := strings.Split(modelPaths[i], string(filepath.Separator))
|
|
|
|
svc := parts[len(parts)-3]
|
|
|
|
|
|
|
|
if _, ok := m[svc]; ok {
|
|
|
|
// Removed unused service version
|
|
|
|
exclude = append(exclude, modelPaths[i])
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
include = append(include, modelPaths[i])
|
|
|
|
m[svc] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return include, exclude
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Attach opens a file by name, and unmarshal its JSON data.
|
|
|
|
// Will proceed to setup the API if not already done so.
|
|
|
|
func (a *API) Attach(filename string) {
|
|
|
|
a.path = filepath.Dir(filename)
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
defer f.Close()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if err := json.NewDecoder(f).Decode(a); err != nil {
|
|
|
|
panic(fmt.Errorf("failed to decode %s, err: %v", filename, err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AttachString will unmarshal a raw JSON string, and setup the
|
|
|
|
// API if not already done so.
|
|
|
|
func (a *API) AttachString(str string) {
|
|
|
|
json.Unmarshal([]byte(str), a)
|
|
|
|
|
|
|
|
if !a.initialized {
|
|
|
|
a.Setup()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup initializes the API.
|
|
|
|
func (a *API) Setup() {
|
2019-01-21 14:27:20 +00:00
|
|
|
a.setServiceAliaseName()
|
2017-12-08 12:03:10 +00:00
|
|
|
a.setMetadataEndpointsKey()
|
|
|
|
a.writeShapeNames()
|
|
|
|
a.resolveReferences()
|
2019-01-21 14:27:20 +00:00
|
|
|
|
|
|
|
if !a.NoRemoveUnusedShapes {
|
|
|
|
a.removeUnusedShapes()
|
2017-12-08 12:03:10 +00:00
|
|
|
}
|
2017-12-29 22:13:47 +00:00
|
|
|
|
2019-01-21 14:27:20 +00:00
|
|
|
a.fixStutterNames()
|
|
|
|
a.renameExportable()
|
|
|
|
a.applyShapeNameAliases()
|
|
|
|
a.createInputOutputShapes()
|
|
|
|
a.renameAPIPayloadShapes()
|
2017-12-29 22:13:47 +00:00
|
|
|
a.renameCollidingFields()
|
2017-12-08 12:03:10 +00:00
|
|
|
a.updateTopLevelShapeReferences()
|
2019-01-21 14:27:20 +00:00
|
|
|
a.suppressHTTP2EventStreams()
|
|
|
|
a.setupEventStreams()
|
|
|
|
a.findEndpointDiscoveryOp()
|
2017-12-08 12:03:10 +00:00
|
|
|
a.customizationPasses()
|
|
|
|
|
|
|
|
if !a.NoRemoveUnusedShapes {
|
|
|
|
a.removeUnusedShapes()
|
|
|
|
}
|
|
|
|
|
|
|
|
if !a.NoValidataShapeMethods {
|
|
|
|
a.addShapeValidations()
|
|
|
|
}
|
|
|
|
|
|
|
|
a.initialized = true
|
|
|
|
}
|