load.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // +build codegen
  2. package api
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "sort"
  9. "strings"
  10. )
  11. // APIs provides a set of API models loaded by API package name.
  12. type APIs map[string]*API
  13. // LoadAPIs loads the API model files from disk returning the map of API
  14. // package. Returns error if multiple API model resolve to the same package
  15. // name.
  16. func LoadAPIs(modelPaths []string, baseImport string) (APIs, error) {
  17. apis := APIs{}
  18. for _, modelPath := range modelPaths {
  19. a, err := loadAPI(modelPath, baseImport)
  20. if err != nil {
  21. return nil, fmt.Errorf("failed to load API, %v, %v", modelPath, err)
  22. }
  23. importPath := a.ImportPath()
  24. if _, ok := apis[importPath]; ok {
  25. return nil, fmt.Errorf(
  26. "package names must be unique attempted to load %v twice. Second model file: %v",
  27. importPath, modelPath)
  28. }
  29. apis[importPath] = a
  30. }
  31. return apis, nil
  32. }
  33. func loadAPI(modelPath, baseImport string) (*API, error) {
  34. a := &API{
  35. BaseImportPath: baseImport,
  36. BaseCrosslinkURL: "https://docs.aws.amazon.com",
  37. }
  38. modelFile := filepath.Base(modelPath)
  39. modelDir := filepath.Dir(modelPath)
  40. err := attachModelFiles(modelDir,
  41. modelLoader{modelFile, a.Attach, true},
  42. modelLoader{"docs-2.json", a.AttachDocs, false},
  43. modelLoader{"paginators-1.json", a.AttachPaginators, false},
  44. modelLoader{"waiters-2.json", a.AttachWaiters, false},
  45. modelLoader{"examples-1.json", a.AttachExamples, false},
  46. modelLoader{"smoke.json", a.AttachSmokeTests, false},
  47. )
  48. if err != nil {
  49. return nil, err
  50. }
  51. a.Setup()
  52. return a, nil
  53. }
  54. type modelLoader struct {
  55. Filename string
  56. Loader func(string)
  57. Required bool
  58. }
  59. func attachModelFiles(modelPath string, modelFiles ...modelLoader) error {
  60. for _, m := range modelFiles {
  61. filepath := filepath.Join(modelPath, m.Filename)
  62. _, err := os.Stat(filepath)
  63. if os.IsNotExist(err) && !m.Required {
  64. continue
  65. } else if err != nil {
  66. return fmt.Errorf("failed to load model file %v, %v", m.Filename, err)
  67. }
  68. m.Loader(filepath)
  69. }
  70. return nil
  71. }
  72. // ExpandModelGlobPath returns a slice of model paths expanded from the glob
  73. // pattern passed in. Returns the path of the model file to be loaded. Includes
  74. // all versions of a service model.
  75. //
  76. // e.g:
  77. // models/apis/*/*/api-2.json
  78. //
  79. // Or with specific model file:
  80. // models/apis/service/version/api-2.json
  81. func ExpandModelGlobPath(globs ...string) ([]string, error) {
  82. modelPaths := []string{}
  83. for _, g := range globs {
  84. filepaths, err := filepath.Glob(g)
  85. if err != nil {
  86. return nil, err
  87. }
  88. for _, p := range filepaths {
  89. modelPaths = append(modelPaths, p)
  90. }
  91. }
  92. return modelPaths, nil
  93. }
  94. // TrimModelServiceVersions sorts the model paths by service version then
  95. // returns recent model versions, and model version excluded.
  96. //
  97. // Uses the third from last path element to determine unique service. Only one
  98. // service version will be included.
  99. //
  100. // models/apis/service/version/api-2.json
  101. func TrimModelServiceVersions(modelPaths []string) (include, exclude []string) {
  102. sort.Strings(modelPaths)
  103. // Remove old API versions from list
  104. m := map[string]struct{}{}
  105. for i := len(modelPaths) - 1; i >= 0; i-- {
  106. // service name is 2nd-to-last component
  107. parts := strings.Split(modelPaths[i], string(filepath.Separator))
  108. svc := parts[len(parts)-3]
  109. if _, ok := m[svc]; ok {
  110. // Removed unused service version
  111. exclude = append(exclude, modelPaths[i])
  112. continue
  113. }
  114. include = append(include, modelPaths[i])
  115. m[svc] = struct{}{}
  116. }
  117. return include, exclude
  118. }
  119. // Attach opens a file by name, and unmarshal its JSON data.
  120. // Will proceed to setup the API if not already done so.
  121. func (a *API) Attach(filename string) {
  122. a.path = filepath.Dir(filename)
  123. f, err := os.Open(filename)
  124. defer f.Close()
  125. if err != nil {
  126. panic(err)
  127. }
  128. if err := json.NewDecoder(f).Decode(a); err != nil {
  129. panic(fmt.Errorf("failed to decode %s, err: %v", filename, err))
  130. }
  131. }
  132. // AttachString will unmarshal a raw JSON string, and setup the
  133. // API if not already done so.
  134. func (a *API) AttachString(str string) {
  135. json.Unmarshal([]byte(str), a)
  136. if !a.initialized {
  137. a.Setup()
  138. }
  139. }
  140. // Setup initializes the API.
  141. func (a *API) Setup() {
  142. a.setServiceAliaseName()
  143. a.setMetadataEndpointsKey()
  144. a.writeShapeNames()
  145. a.resolveReferences()
  146. if !a.NoRemoveUnusedShapes {
  147. a.removeUnusedShapes()
  148. }
  149. a.fixStutterNames()
  150. a.renameExportable()
  151. a.applyShapeNameAliases()
  152. a.createInputOutputShapes()
  153. a.renameAPIPayloadShapes()
  154. a.renameCollidingFields()
  155. a.updateTopLevelShapeReferences()
  156. a.suppressHTTP2EventStreams()
  157. a.setupEventStreams()
  158. a.findEndpointDiscoveryOp()
  159. a.customizationPasses()
  160. if !a.NoRemoveUnusedShapes {
  161. a.removeUnusedShapes()
  162. }
  163. if !a.NoValidataShapeMethods {
  164. a.addShapeValidations()
  165. }
  166. a.initialized = true
  167. }