customization_passes.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // +build codegen
  2. package api
  3. import (
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. )
  10. type service struct {
  11. srcName string
  12. dstName string
  13. serviceVersion string
  14. }
  15. var mergeServices = map[string]service{
  16. "dynamodbstreams": {
  17. dstName: "dynamodb",
  18. srcName: "streams.dynamodb",
  19. },
  20. "wafregional": {
  21. dstName: "waf",
  22. srcName: "waf-regional",
  23. serviceVersion: "2015-08-24",
  24. },
  25. }
  26. var serviceAliaseNames = map[string]string{
  27. "costandusagereportservice": "CostandUsageReportService",
  28. "elasticloadbalancing": "ELB",
  29. "elasticloadbalancingv2": "ELBV2",
  30. "config": "ConfigService",
  31. }
  32. func (a *API) setServiceAliaseName() {
  33. if newName, ok := serviceAliaseNames[a.PackageName()]; ok {
  34. a.name = newName
  35. }
  36. }
  37. // customizationPasses Executes customization logic for the API by package name.
  38. func (a *API) customizationPasses() {
  39. var svcCustomizations = map[string]func(*API){
  40. "s3": s3Customizations,
  41. "s3control": s3ControlCustomizations,
  42. "cloudfront": cloudfrontCustomizations,
  43. "rds": rdsCustomizations,
  44. // Disable endpoint resolving for services that require customer
  45. // to provide endpoint them selves.
  46. "cloudsearchdomain": disableEndpointResolving,
  47. "iotdataplane": disableEndpointResolving,
  48. // MTurk smoke test is invalid. The service requires AWS account to be
  49. // linked to Amazon Mechanical Turk Account.
  50. "mturk": supressSmokeTest,
  51. }
  52. for k := range mergeServices {
  53. svcCustomizations[k] = mergeServicesCustomizations
  54. }
  55. if fn := svcCustomizations[a.PackageName()]; fn != nil {
  56. fn(a)
  57. }
  58. }
  59. func supressSmokeTest(a *API) {
  60. a.SmokeTests.TestCases = []SmokeTestCase{}
  61. }
  62. // s3Customizations customizes the API generation to replace values specific to S3.
  63. func s3Customizations(a *API) {
  64. var strExpires *Shape
  65. var keepContentMD5Ref = map[string]struct{}{
  66. "PutObjectInput": {},
  67. "UploadPartInput": {},
  68. }
  69. for name, s := range a.Shapes {
  70. // Remove ContentMD5 members unless specified otherwise.
  71. if _, keep := keepContentMD5Ref[name]; !keep {
  72. if _, have := s.MemberRefs["ContentMD5"]; have {
  73. delete(s.MemberRefs, "ContentMD5")
  74. }
  75. }
  76. // Generate getter methods for API operation fields used by customizations.
  77. for _, refName := range []string{"Bucket", "SSECustomerKey", "CopySourceSSECustomerKey"} {
  78. if ref, ok := s.MemberRefs[refName]; ok {
  79. ref.GenerateGetter = true
  80. }
  81. }
  82. // Expires should be a string not time.Time since the format is not
  83. // enforced by S3, and any value can be set to this field outside of the SDK.
  84. if strings.HasSuffix(name, "Output") {
  85. if ref, ok := s.MemberRefs["Expires"]; ok {
  86. if strExpires == nil {
  87. newShape := *ref.Shape
  88. strExpires = &newShape
  89. strExpires.Type = "string"
  90. strExpires.refs = []*ShapeRef{}
  91. }
  92. ref.Shape.removeRef(ref)
  93. ref.Shape = strExpires
  94. ref.Shape.refs = append(ref.Shape.refs, &s.MemberRef)
  95. }
  96. }
  97. }
  98. s3CustRemoveHeadObjectModeledErrors(a)
  99. }
  100. // S3 HeadObject API call incorrect models NoSuchKey as valid
  101. // error code that can be returned. This operation does not
  102. // return error codes, all error codes are derived from HTTP
  103. // status codes.
  104. //
  105. // aws/aws-sdk-go#1208
  106. func s3CustRemoveHeadObjectModeledErrors(a *API) {
  107. op, ok := a.Operations["HeadObject"]
  108. if !ok {
  109. return
  110. }
  111. op.Documentation += `
  112. //
  113. // See http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html#RESTErrorResponses
  114. // for more information on returned errors.`
  115. op.ErrorRefs = []ShapeRef{}
  116. }
  117. // S3 service operations with an AccountId need accessors to be generated for
  118. // them so the fields can be dynamically accessed without reflection.
  119. func s3ControlCustomizations(a *API) {
  120. for opName, op := range a.Operations {
  121. // Add moving AccountId into the hostname instead of header.
  122. if ref, ok := op.InputRef.Shape.MemberRefs["AccountId"]; ok {
  123. if op.Endpoint != nil {
  124. fmt.Fprintf(os.Stderr, "S3 Control, %s, model already defining endpoint trait, remove this customization.\n", opName)
  125. }
  126. op.Endpoint = &EndpointTrait{HostPrefix: "{AccountId}."}
  127. ref.HostLabel = true
  128. }
  129. }
  130. }
  131. // cloudfrontCustomizations customized the API generation to replace values
  132. // specific to CloudFront.
  133. func cloudfrontCustomizations(a *API) {
  134. // MaxItems members should always be integers
  135. for _, s := range a.Shapes {
  136. if ref, ok := s.MemberRefs["MaxItems"]; ok {
  137. ref.ShapeName = "Integer"
  138. ref.Shape = a.Shapes["Integer"]
  139. }
  140. }
  141. }
  142. // mergeServicesCustomizations references any duplicate shapes from DynamoDB
  143. func mergeServicesCustomizations(a *API) {
  144. info := mergeServices[a.PackageName()]
  145. p := strings.Replace(a.path, info.srcName, info.dstName, -1)
  146. if info.serviceVersion != "" {
  147. index := strings.LastIndex(p, string(filepath.Separator))
  148. files, _ := ioutil.ReadDir(p[:index])
  149. if len(files) > 1 {
  150. panic("New version was introduced")
  151. }
  152. p = p[:index] + "/" + info.serviceVersion
  153. }
  154. file := filepath.Join(p, "api-2.json")
  155. serviceAPI := API{}
  156. serviceAPI.Attach(file)
  157. serviceAPI.Setup()
  158. for n := range a.Shapes {
  159. if _, ok := serviceAPI.Shapes[n]; ok {
  160. a.Shapes[n].resolvePkg = SDKImportRoot + "/service/" + info.dstName
  161. }
  162. }
  163. }
  164. // rdsCustomizations are customization for the service/rds. This adds non-modeled fields used for presigning.
  165. func rdsCustomizations(a *API) {
  166. inputs := []string{
  167. "CopyDBSnapshotInput",
  168. "CreateDBInstanceReadReplicaInput",
  169. "CopyDBClusterSnapshotInput",
  170. "CreateDBClusterInput",
  171. }
  172. for _, input := range inputs {
  173. if ref, ok := a.Shapes[input]; ok {
  174. ref.MemberRefs["SourceRegion"] = &ShapeRef{
  175. 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.`),
  176. ShapeName: "String",
  177. Shape: a.Shapes["String"],
  178. Ignore: true,
  179. }
  180. ref.MemberRefs["DestinationRegion"] = &ShapeRef{
  181. Documentation: docstring(`DestinationRegion is used for presigning the request to a given region.`),
  182. ShapeName: "String",
  183. Shape: a.Shapes["String"],
  184. }
  185. }
  186. }
  187. }
  188. func disableEndpointResolving(a *API) {
  189. a.Metadata.NoResolveEndpoint = true
  190. }