customization_passes.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // +build codegen
  2. package api
  3. import (
  4. "fmt"
  5. "io/ioutil"
  6. "path/filepath"
  7. "strings"
  8. )
  9. type service struct {
  10. srcName string
  11. dstName string
  12. serviceVersion string
  13. }
  14. var mergeServices = map[string]service{
  15. "dynamodbstreams": service{
  16. dstName: "dynamodb",
  17. srcName: "streams.dynamodb",
  18. },
  19. "wafregional": service{
  20. dstName: "waf",
  21. srcName: "waf-regional",
  22. serviceVersion: "2015-08-24",
  23. },
  24. }
  25. // customizationPasses Executes customization logic for the API by package name.
  26. func (a *API) customizationPasses() {
  27. var svcCustomizations = map[string]func(*API){
  28. "s3": s3Customizations,
  29. "cloudfront": cloudfrontCustomizations,
  30. "rds": rdsCustomizations,
  31. // Disable endpoint resolving for services that require customer
  32. // to provide endpoint them selves.
  33. "cloudsearchdomain": disableEndpointResolving,
  34. "iotdataplane": disableEndpointResolving,
  35. }
  36. for k, _ := range mergeServices {
  37. svcCustomizations[k] = mergeServicesCustomizations
  38. }
  39. if fn := svcCustomizations[a.PackageName()]; fn != nil {
  40. fn(a)
  41. }
  42. blobDocStringCustomizations(a)
  43. }
  44. const base64MarshalDocStr = "// %s is automatically base64 encoded/decoded by the SDK.\n"
  45. func blobDocStringCustomizations(a *API) {
  46. for _, s := range a.Shapes {
  47. payloadMemberName := s.Payload
  48. for refName, ref := range s.MemberRefs {
  49. if refName == payloadMemberName {
  50. // Payload members have their own encoding and may
  51. // be raw bytes or io.Reader
  52. continue
  53. }
  54. if ref.Shape.Type == "blob" {
  55. docStr := fmt.Sprintf(base64MarshalDocStr, refName)
  56. if len(strings.TrimSpace(ref.Shape.Documentation)) != 0 {
  57. ref.Shape.Documentation += "//\n" + docStr
  58. } else if len(strings.TrimSpace(ref.Documentation)) != 0 {
  59. ref.Documentation += "//\n" + docStr
  60. } else {
  61. ref.Documentation = docStr
  62. }
  63. }
  64. }
  65. }
  66. }
  67. // s3Customizations customizes the API generation to replace values specific to S3.
  68. func s3Customizations(a *API) {
  69. var strExpires *Shape
  70. for name, s := range a.Shapes {
  71. // Remove ContentMD5 members
  72. if _, ok := s.MemberRefs["ContentMD5"]; ok {
  73. delete(s.MemberRefs, "ContentMD5")
  74. }
  75. // Expires should be a string not time.Time since the format is not
  76. // enforced by S3, and any value can be set to this field outside of the SDK.
  77. if strings.HasSuffix(name, "Output") {
  78. if ref, ok := s.MemberRefs["Expires"]; ok {
  79. if strExpires == nil {
  80. newShape := *ref.Shape
  81. strExpires = &newShape
  82. strExpires.Type = "string"
  83. strExpires.refs = []*ShapeRef{}
  84. }
  85. ref.Shape.removeRef(ref)
  86. ref.Shape = strExpires
  87. ref.Shape.refs = append(ref.Shape.refs, &s.MemberRef)
  88. }
  89. }
  90. }
  91. }
  92. // cloudfrontCustomizations customized the API generation to replace values
  93. // specific to CloudFront.
  94. func cloudfrontCustomizations(a *API) {
  95. // MaxItems members should always be integers
  96. for _, s := range a.Shapes {
  97. if ref, ok := s.MemberRefs["MaxItems"]; ok {
  98. ref.ShapeName = "Integer"
  99. ref.Shape = a.Shapes["Integer"]
  100. }
  101. }
  102. }
  103. // mergeServicesCustomizations references any duplicate shapes from DynamoDB
  104. func mergeServicesCustomizations(a *API) {
  105. info := mergeServices[a.PackageName()]
  106. p := strings.Replace(a.path, info.srcName, info.dstName, -1)
  107. if info.serviceVersion != "" {
  108. index := strings.LastIndex(p, "/")
  109. files, _ := ioutil.ReadDir(p[:index])
  110. if len(files) > 1 {
  111. panic("New version was introduced")
  112. }
  113. p = p[:index] + "/" + info.serviceVersion
  114. }
  115. file := filepath.Join(p, "api-2.json")
  116. serviceAPI := API{}
  117. serviceAPI.Attach(file)
  118. serviceAPI.Setup()
  119. for n := range a.Shapes {
  120. if _, ok := serviceAPI.Shapes[n]; ok {
  121. a.Shapes[n].resolvePkg = "github.com/aws/aws-sdk-go/service/" + info.dstName
  122. }
  123. }
  124. }
  125. // rdsCustomizations are customization for the service/rds. This adds non-modeled fields used for presigning.
  126. func rdsCustomizations(a *API) {
  127. inputs := []string{
  128. "CopyDBSnapshotInput",
  129. "CreateDBInstanceReadReplicaInput",
  130. "CopyDBClusterSnapshotInput",
  131. "CreateDBClusterInput",
  132. }
  133. for _, input := range inputs {
  134. if ref, ok := a.Shapes[input]; ok {
  135. ref.MemberRefs["SourceRegion"] = &ShapeRef{
  136. Documentation: docstring(`SourceRegion is the source region where the resource exists. This is not sent over the wire and is only used for presigning. This value should always have the same region as the source ARN.`),
  137. ShapeName: "String",
  138. Shape: a.Shapes["String"],
  139. Ignore: true,
  140. }
  141. ref.MemberRefs["DestinationRegion"] = &ShapeRef{
  142. Documentation: docstring(`DestinationRegion is used for presigning the request to a given region.`),
  143. ShapeName: "String",
  144. Shape: a.Shapes["String"],
  145. }
  146. }
  147. }
  148. }
  149. func disableEndpointResolving(a *API) {
  150. a.Metadata.NoResolveEndpoint = true
  151. }