passes.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. // +build codegen
  2. package api
  3. import (
  4. "fmt"
  5. "regexp"
  6. "strings"
  7. )
  8. // updateTopLevelShapeReferences moves resultWrapper, locationName, and
  9. // xmlNamespace traits from toplevel shape references to the toplevel
  10. // shapes for easier code generation
  11. func (a *API) updateTopLevelShapeReferences() {
  12. for _, o := range a.Operations {
  13. // these are for REST-XML services
  14. if o.InputRef.LocationName != "" {
  15. o.InputRef.Shape.LocationName = o.InputRef.LocationName
  16. }
  17. if o.InputRef.Location != "" {
  18. o.InputRef.Shape.Location = o.InputRef.Location
  19. }
  20. if o.InputRef.Payload != "" {
  21. o.InputRef.Shape.Payload = o.InputRef.Payload
  22. }
  23. if o.InputRef.XMLNamespace.Prefix != "" {
  24. o.InputRef.Shape.XMLNamespace.Prefix = o.InputRef.XMLNamespace.Prefix
  25. }
  26. if o.InputRef.XMLNamespace.URI != "" {
  27. o.InputRef.Shape.XMLNamespace.URI = o.InputRef.XMLNamespace.URI
  28. }
  29. }
  30. }
  31. // writeShapeNames sets each shape's API and shape name values. Binding the
  32. // shape to its parent API.
  33. func (a *API) writeShapeNames() {
  34. for n, s := range a.Shapes {
  35. s.API = a
  36. s.ShapeName = n
  37. }
  38. }
  39. func (a *API) resolveReferences() {
  40. resolver := referenceResolver{API: a, visited: map[*ShapeRef]bool{}}
  41. for _, s := range a.Shapes {
  42. resolver.resolveShape(s)
  43. }
  44. for _, o := range a.Operations {
  45. o.API = a // resolve parent reference
  46. resolver.resolveReference(&o.InputRef)
  47. resolver.resolveReference(&o.OutputRef)
  48. // Resolve references for errors also
  49. for i := range o.ErrorRefs {
  50. resolver.resolveReference(&o.ErrorRefs[i])
  51. o.ErrorRefs[i].Shape.IsError = true
  52. o.ErrorRefs[i].Shape.ErrorInfo.Type = o.ErrorRefs[i].Shape.ShapeName
  53. }
  54. }
  55. }
  56. // A referenceResolver provides a way to resolve shape references to
  57. // shape definitions.
  58. type referenceResolver struct {
  59. *API
  60. visited map[*ShapeRef]bool
  61. }
  62. // resolveReference updates a shape reference to reference the API and
  63. // its shape definition. All other nested references are also resolved.
  64. func (r *referenceResolver) resolveReference(ref *ShapeRef) {
  65. if ref.ShapeName == "" {
  66. return
  67. }
  68. shape, ok := r.API.Shapes[ref.ShapeName]
  69. if !ok {
  70. panic(fmt.Sprintf("unable resolve reference, %s", ref.ShapeName))
  71. }
  72. if ref.JSONValue {
  73. ref.ShapeName = "JSONValue"
  74. if _, ok := r.API.Shapes[ref.ShapeName]; !ok {
  75. r.API.Shapes[ref.ShapeName] = &Shape{
  76. API: r.API,
  77. ShapeName: "JSONValue",
  78. Type: "jsonvalue",
  79. ValueRef: ShapeRef{
  80. JSONValue: true,
  81. },
  82. }
  83. }
  84. }
  85. ref.API = r.API // resolve reference back to API
  86. ref.Shape = shape // resolve shape reference
  87. if r.visited[ref] {
  88. return
  89. }
  90. r.visited[ref] = true
  91. shape.refs = append(shape.refs, ref) // register the ref
  92. // resolve shape's references, if it has any
  93. r.resolveShape(shape)
  94. }
  95. // resolveShape resolves a shape's Member Key Value, and nested member
  96. // shape references.
  97. func (r *referenceResolver) resolveShape(shape *Shape) {
  98. r.resolveReference(&shape.MemberRef)
  99. r.resolveReference(&shape.KeyRef)
  100. r.resolveReference(&shape.ValueRef)
  101. for _, m := range shape.MemberRefs {
  102. r.resolveReference(m)
  103. }
  104. }
  105. // fixStutterNames fixes all name struttering based on Go naming conventions.
  106. // "Stuttering" is when the prefix of a structure or function matches the
  107. // package name (case insensitive).
  108. func (a *API) fixStutterNames() {
  109. str, end := a.StructName(), ""
  110. if len(str) > 1 {
  111. l := len(str) - 1
  112. str, end = str[0:l], str[l:]
  113. }
  114. re := regexp.MustCompile(fmt.Sprintf(`\A(?i:%s)%s`, str, end))
  115. for name, op := range a.Operations {
  116. newName := re.ReplaceAllString(name, "")
  117. if newName != name && len(newName) > 0 {
  118. delete(a.Operations, name)
  119. a.Operations[newName] = op
  120. }
  121. op.ExportedName = newName
  122. }
  123. for k, s := range a.Shapes {
  124. newName := re.ReplaceAllString(k, "")
  125. if newName != s.ShapeName && len(newName) > 0 {
  126. s.Rename(newName)
  127. }
  128. }
  129. }
  130. // renameExportable renames all operation names to be exportable names.
  131. // All nested Shape names are also updated to the exportable variant.
  132. func (a *API) renameExportable() {
  133. for name, op := range a.Operations {
  134. newName := a.ExportableName(name)
  135. if newName != name {
  136. delete(a.Operations, name)
  137. a.Operations[newName] = op
  138. }
  139. op.ExportedName = newName
  140. }
  141. for k, s := range a.Shapes {
  142. // FIXME SNS has lower and uppercased shape names with the same name,
  143. // except the lowercased variant is used exclusively for string and
  144. // other primitive types. Renaming both would cause a collision.
  145. // We work around this by only renaming the structure shapes.
  146. if s.Type == "string" {
  147. continue
  148. }
  149. for mName, member := range s.MemberRefs {
  150. ref := s.MemberRefs[mName]
  151. ref.OrigShapeName = mName
  152. s.MemberRefs[mName] = ref
  153. newName := a.ExportableName(mName)
  154. if newName != mName {
  155. delete(s.MemberRefs, mName)
  156. s.MemberRefs[newName] = member
  157. // also apply locationName trait so we keep the old one
  158. // but only if there's no locationName trait on ref or shape
  159. if member.LocationName == "" && member.Shape.LocationName == "" {
  160. member.LocationName = mName
  161. }
  162. }
  163. if newName == "_" {
  164. panic("Shape " + s.ShapeName + " uses reserved member name '_'")
  165. }
  166. }
  167. newName := a.ExportableName(k)
  168. if newName != s.ShapeName {
  169. s.Rename(newName)
  170. }
  171. s.Payload = a.ExportableName(s.Payload)
  172. // fix required trait names
  173. for i, n := range s.Required {
  174. s.Required[i] = a.ExportableName(n)
  175. }
  176. }
  177. for _, s := range a.Shapes {
  178. // fix enum names
  179. if s.IsEnum() {
  180. s.EnumConsts = make([]string, len(s.Enum))
  181. for i := range s.Enum {
  182. shape := s.ShapeName
  183. shape = strings.ToUpper(shape[0:1]) + shape[1:]
  184. s.EnumConsts[i] = shape + s.EnumName(i)
  185. }
  186. }
  187. }
  188. }
  189. // renameCollidingFields will rename any fields that uses an SDK or Golang
  190. // specific name.
  191. func (a *API) renameCollidingFields() {
  192. for _, v := range a.Shapes {
  193. namesWithSet := map[string]struct{}{}
  194. for k, field := range v.MemberRefs {
  195. if _, ok := v.MemberRefs["Set"+k]; ok {
  196. namesWithSet["Set"+k] = struct{}{}
  197. }
  198. if collides(k) || (v.Exception && exceptionCollides(k)) {
  199. renameCollidingField(k, v, field)
  200. }
  201. }
  202. // checks if any field names collide with setters.
  203. for name := range namesWithSet {
  204. field := v.MemberRefs[name]
  205. renameCollidingField(name, v, field)
  206. }
  207. }
  208. }
  209. func renameCollidingField(name string, v *Shape, field *ShapeRef) {
  210. newName := name + "_"
  211. debugLogger.Logf("Shape %s's field %q renamed to %q", v.ShapeName, name, newName)
  212. delete(v.MemberRefs, name)
  213. v.MemberRefs[newName] = field
  214. }
  215. // collides will return true if it is a name used by the SDK or Golang.
  216. func collides(name string) bool {
  217. switch name {
  218. case "String",
  219. "GoString",
  220. "Validate":
  221. return true
  222. }
  223. return false
  224. }
  225. func exceptionCollides(name string) bool {
  226. switch name {
  227. case "Code",
  228. "Message",
  229. "OrigErr":
  230. return true
  231. }
  232. return false
  233. }
  234. func (a *API) applyShapeNameAliases() {
  235. service, ok := shapeNameAliases[a.name]
  236. if !ok {
  237. return
  238. }
  239. // Generic Shape Aliases
  240. for name, s := range a.Shapes {
  241. if alias, ok := service[name]; ok {
  242. s.Rename(alias)
  243. s.AliasedShapeName = true
  244. }
  245. }
  246. }
  247. // createInputOutputShapes creates toplevel input/output shapes if they
  248. // have not been defined in the API. This normalizes all APIs to always
  249. // have an input and output structure in the signature.
  250. func (a *API) createInputOutputShapes() {
  251. for _, op := range a.Operations {
  252. createAPIParamShape(a, op.Name, &op.InputRef, op.ExportedName+"Input",
  253. shamelist.Input,
  254. )
  255. createAPIParamShape(a, op.Name, &op.OutputRef, op.ExportedName+"Output",
  256. shamelist.Output,
  257. )
  258. }
  259. }
  260. func (a *API) renameAPIPayloadShapes() {
  261. for _, op := range a.Operations {
  262. op.InputRef.Payload = a.ExportableName(op.InputRef.Payload)
  263. op.OutputRef.Payload = a.ExportableName(op.OutputRef.Payload)
  264. }
  265. }
  266. func createAPIParamShape(a *API, opName string, ref *ShapeRef, shapeName string, shamelistLookup func(string, string) bool) {
  267. if len(ref.ShapeName) == 0 {
  268. setAsPlacholderShape(ref, shapeName, a)
  269. return
  270. }
  271. // nothing to do if already the correct name.
  272. if s := ref.Shape; s.AliasedShapeName || s.ShapeName == shapeName || shamelistLookup(a.name, opName) {
  273. return
  274. }
  275. if s, ok := a.Shapes[shapeName]; ok {
  276. panic(fmt.Sprintf(
  277. "attempting to create duplicate API parameter shape, %v, %v, %v, %v\n",
  278. shapeName, opName, ref.ShapeName, s.OrigShapeName,
  279. ))
  280. }
  281. ref.Shape.removeRef(ref)
  282. ref.OrigShapeName = shapeName
  283. ref.ShapeName = shapeName
  284. ref.Shape = ref.Shape.Clone(shapeName)
  285. ref.Shape.refs = append(ref.Shape.refs, ref)
  286. }
  287. func setAsPlacholderShape(tgtShapeRef *ShapeRef, name string, a *API) {
  288. shape := a.makeIOShape(name)
  289. shape.Placeholder = true
  290. *tgtShapeRef = ShapeRef{API: a, ShapeName: shape.ShapeName, Shape: shape}
  291. shape.refs = append(shape.refs, tgtShapeRef)
  292. }
  293. // makeIOShape returns a pointer to a new Shape initialized by the name provided.
  294. func (a *API) makeIOShape(name string) *Shape {
  295. shape := &Shape{
  296. API: a, ShapeName: name, Type: "structure",
  297. MemberRefs: map[string]*ShapeRef{},
  298. }
  299. a.Shapes[name] = shape
  300. return shape
  301. }
  302. // removeUnusedShapes removes shapes from the API which are not referenced by any
  303. // other shape in the API.
  304. func (a *API) removeUnusedShapes() {
  305. for _, s := range a.Shapes {
  306. if len(s.refs) == 0 {
  307. a.removeShape(s)
  308. }
  309. }
  310. }
  311. // Represents the service package name to EndpointsID mapping
  312. var custEndpointsKey = map[string]string{
  313. "applicationautoscaling": "application-autoscaling",
  314. }
  315. // Sents the EndpointsID field of Metadata with the value of the
  316. // EndpointPrefix if EndpointsID is not set. Also adds
  317. // customizations for services if EndpointPrefix is not a valid key.
  318. func (a *API) setMetadataEndpointsKey() {
  319. if len(a.Metadata.EndpointsID) != 0 {
  320. return
  321. }
  322. if v, ok := custEndpointsKey[a.PackageName()]; ok {
  323. a.Metadata.EndpointsID = v
  324. } else {
  325. a.Metadata.EndpointsID = a.Metadata.EndpointPrefix
  326. }
  327. }
  328. // Suppress event stream must be run before setup event stream
  329. func (a *API) suppressHTTP2EventStreams() {
  330. if a.Metadata.ProtocolSettings.HTTP2 != "eventstream" {
  331. return
  332. }
  333. for name, op := range a.Operations {
  334. outbound := hasEventStream(op.InputRef.Shape)
  335. inbound := hasEventStream(op.OutputRef.Shape)
  336. if !(outbound || inbound) {
  337. continue
  338. }
  339. a.removeOperation(name)
  340. }
  341. }
  342. func (a *API) findEndpointDiscoveryOp() {
  343. for _, op := range a.Operations {
  344. if op.IsEndpointDiscoveryOp {
  345. a.EndpointDiscoveryOp = op
  346. return
  347. }
  348. }
  349. }