diff --git a/aws/provider.go b/aws/provider.go index 434652b171c4..62ae320ae2c7 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -239,6 +239,7 @@ func Provider() terraform.ResourceProvider { "aws_api_gateway_method_response": resourceAwsApiGatewayMethodResponse(), "aws_api_gateway_method_settings": resourceAwsApiGatewayMethodSettings(), "aws_api_gateway_model": resourceAwsApiGatewayModel(), + "aws_api_gateway_request_validator": resourceAwsApiGatewayRequestValidator(), "aws_api_gateway_resource": resourceAwsApiGatewayResource(), "aws_api_gateway_rest_api": resourceAwsApiGatewayRestApi(), "aws_api_gateway_stage": resourceAwsApiGatewayStage(), diff --git a/aws/resource_aws_api_gateway_method.go b/aws/resource_aws_api_gateway_method.go index 577c44e1528a..8bf402044089 100644 --- a/aws/resource_aws_api_gateway_method.go +++ b/aws/resource_aws_api_gateway_method.go @@ -76,6 +76,11 @@ func resourceAwsApiGatewayMethod() *schema.Resource { ConflictsWith: []string{"request_parameters"}, Deprecated: "Use field request_parameters instead", }, + + "request_validator_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, }, } } @@ -121,6 +126,10 @@ func resourceAwsApiGatewayMethodCreate(d *schema.ResourceData, meta interface{}) input.AuthorizerId = aws.String(v.(string)) } + if v, ok := d.GetOk("request_validator_id"); ok { + input.RequestValidatorId = aws.String(v.(string)) + } + _, err := conn.PutMethod(&input) if err != nil { return fmt.Errorf("Error creating API Gateway Method: %s", err) @@ -156,6 +165,7 @@ func resourceAwsApiGatewayMethodRead(d *schema.ResourceData, meta interface{}) e d.Set("authorization_type", out.AuthorizationType) d.Set("authorizer_id", out.AuthorizerId) d.Set("request_models", aws.StringValueMap(out.RequestModels)) + d.Set("request_validator_id", out.RequestValidatorId) return nil } @@ -226,6 +236,22 @@ func resourceAwsApiGatewayMethodUpdate(d *schema.ResourceData, meta interface{}) }) } + if d.HasChange("request_validator_id") { + var request_validator_id *string + if v, ok := d.GetOk("request_validator_id"); ok { + // requestValidatorId cannot be an empty string; it must either be nil + // or it must have some value. Otherwise, updating fails. + if s := v.(string); len(s) > 0 { + request_validator_id = &s + } + } + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String("/requestValidatorId"), + Value: request_validator_id, + }) + } + method, err := conn.UpdateMethod(&apigateway.UpdateMethodInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), diff --git a/aws/resource_aws_api_gateway_method_test.go b/aws/resource_aws_api_gateway_method_test.go index 34f3e0134535..09dd75c8e0bc 100644 --- a/aws/resource_aws_api_gateway_method_test.go +++ b/aws/resource_aws_api_gateway_method_test.go @@ -87,6 +87,44 @@ func TestAccAWSAPIGatewayMethod_customauthorizer(t *testing.T) { }) } +func TestAccAWSAPIGatewayMethod_customrequestvalidator(t *testing.T) { + var conf apigateway.Method + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayMethodDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAPIGatewayMethodConfigWithCustomRequestValidator(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf), + testAccCheckAWSAPIGatewayMethodAttributes(&conf), + resource.TestCheckResourceAttr( + "aws_api_gateway_method.test", "http_method", "GET"), + resource.TestCheckResourceAttr( + "aws_api_gateway_method.test", "authorization", "NONE"), + resource.TestCheckResourceAttr( + "aws_api_gateway_method.test", "request_models.application/json", "Error"), + resource.TestMatchResourceAttr( + "aws_api_gateway_method.test", "request_validator_id", regexp.MustCompile("^[a-z0-9]{6}$")), + ), + }, + + { + Config: testAccAWSAPIGatewayMethodConfigWithCustomRequestValidatorUpdate(rInt), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayMethodExists("aws_api_gateway_method.test", &conf), + testAccCheckAWSAPIGatewayMethodAttributesUpdate(&conf), + resource.TestCheckResourceAttr( + "aws_api_gateway_method.test", "request_validator_id", ""), + ), + }, + }, + }) +} + func testAccCheckAWSAPIGatewayMethodAttributes(conf *apigateway.Method) resource.TestCheckFunc { return func(s *terraform.State) error { if *conf.HttpMethod != "GET" { @@ -357,3 +395,76 @@ resource "aws_api_gateway_method" "test" { } `, rInt) } + +func testAccAWSAPIGatewayMethodConfigWithCustomRequestValidator(rInt int) string { + return fmt.Sprintf(` +resource "aws_api_gateway_rest_api" "test" { + name = "tf-acc-test-apig-method-custom-req-validator-%d" +} + +resource "aws_api_gateway_resource" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}" + path_part = "test" +} + +resource "aws_api_gateway_request_validator" "validator" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + name = "paramsValidator" + validate_request_parameters = true +} + +resource "aws_api_gateway_method" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "GET" + authorization = "NONE" + + request_models = { + "application/json" = "Error" + } + + request_parameters = { + "method.request.header.Content-Type" = false, + "method.request.querystring.page" = true + } + + request_validator_id = "${aws_api_gateway_request_validator.validator.id}" +} +`, rInt) +} + +func testAccAWSAPIGatewayMethodConfigWithCustomRequestValidatorUpdate(rInt int) string { + return fmt.Sprintf(` +resource "aws_api_gateway_rest_api" "test" { + name = "tf-acc-test-apig-method-custom-req-validator-%d" +} + +resource "aws_api_gateway_resource" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + parent_id = "${aws_api_gateway_rest_api.test.root_resource_id}" + path_part = "test" +} + +resource "aws_api_gateway_request_validator" "validator" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + name = "paramsValidator" + validate_request_parameters = true +} + +resource "aws_api_gateway_method" "test" { + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + resource_id = "${aws_api_gateway_resource.test.id}" + http_method = "GET" + authorization = "NONE" + + request_models = { + "application/json" = "Error" + } + + request_parameters = { + "method.request.querystring.page" = false + } +} +`, rInt) +} diff --git a/aws/resource_aws_api_gateway_request_validator.go b/aws/resource_aws_api_gateway_request_validator.go new file mode 100644 index 000000000000..c86d12468ff6 --- /dev/null +++ b/aws/resource_aws_api_gateway_request_validator.go @@ -0,0 +1,154 @@ +package aws + +import ( + "fmt" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/terraform/helper/schema" + "log" + "strings" +) + +func resourceAwsApiGatewayRequestValidator() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsApiGatewayRequestValidatorCreate, + Read: resourceAwsApiGatewayRequestValidatorRead, + Update: resourceAwsApiGatewayRequestValidatorUpdate, + Delete: resourceAwsApiGatewayRequestValidatorDelete, + + Schema: map[string]*schema.Schema{ + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + }, + + "validate_request_body": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "validate_request_parameters": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + } +} + +func resourceAwsApiGatewayRequestValidatorCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + + input := apigateway.CreateRequestValidatorInput{ + Name: aws.String(d.Get("name").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + ValidateRequestBody: aws.Bool(d.Get("validate_request_body").(bool)), + ValidateRequestParameters: aws.Bool(d.Get("validate_request_parameters").(bool)), + } + + out, err := conn.CreateRequestValidator(&input) + if err != nil { + return fmt.Errorf("Error creating Request Validator: %s", err) + } + + d.SetId(*out.Id) + + return nil +} + +func resourceAwsApiGatewayRequestValidatorRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + + input := apigateway.GetRequestValidatorInput{ + RequestValidatorId: aws.String(d.Id()), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + } + + out, err := conn.GetRequestValidator(&input) + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == apigateway.ErrCodeNotFoundException { + d.SetId("") + return nil + } + return err + } + + d.Set("name", out.Name) + d.Set("validate_request_body", out.ValidateRequestBody) + d.Set("validate_request_parameters", out.ValidateRequestParameters) + + return nil +} + +func resourceAwsApiGatewayRequestValidatorUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + log.Printf("[DEBUG] Updating Request Validator %s", d.Id()) + + operations := make([]*apigateway.PatchOperation, 0) + + if d.HasChange("name") { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String("/name"), + Value: aws.String(d.Get("name").(string)), + }) + } + + if d.HasChange("validate_request_body") { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String("/validateRequestBody"), + Value: aws.String(fmt.Sprintf("%t", d.Get("validate_request_body").(bool))), + }) + } + + if d.HasChange("validate_request_parameters") { + operations = append(operations, &apigateway.PatchOperation{ + Op: aws.String("replace"), + Path: aws.String("/validateRequestParameters"), + Value: aws.String(fmt.Sprintf("%t", d.Get("validate_request_parameters").(bool))), + }) + } + + input := apigateway.UpdateRequestValidatorInput{ + RequestValidatorId: aws.String(d.Id()), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + PatchOperations: operations, + } + + _, err := conn.UpdateRequestValidator(&input) + if err != nil { + return err + } + + log.Printf("[DEBUG] Updated Request Validator %s", d.Id()) + + return resourceAwsApiGatewayRequestValidatorRead(d, meta) +} + +func resourceAwsApiGatewayRequestValidatorDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).apigateway + log.Printf("[DEBUG] Deleting Request Validator %s", d.Id()) + + _, err := conn.DeleteRequestValidator(&apigateway.DeleteRequestValidatorInput{ + RequestValidatorId: aws.String(d.Id()), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + }) + if err != nil { + // XXX: Figure out a way to delete the method that depends on the request validator first + // otherwise the validator will be dangling until the API is deleted + if !strings.Contains(err.Error(), apigateway.ErrCodeConflictException) { + return fmt.Errorf("Deleting Request Validator failed: %s", err) + } + } + + return nil +} diff --git a/aws/resource_aws_api_gateway_request_validator_test.go b/aws/resource_aws_api_gateway_request_validator_test.go new file mode 100644 index 000000000000..1f7c81162b6b --- /dev/null +++ b/aws/resource_aws_api_gateway_request_validator_test.go @@ -0,0 +1,166 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSAPIGatewayRequestValidator_basic(t *testing.T) { + var conf apigateway.UpdateRequestValidatorOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayRequestValidatorDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSAPIGatewayRequestValidatorConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayRequestValidatorExists("aws_api_gateway_request_validator.test", &conf), + testAccCheckAWSAPIGatewayRequestValidatorName(&conf, "tf-acc-test-request-validator"), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "name", "tf-acc-test-request-validator"), + testAccCheckAWSAPIGatewayRequestValidatorValidateRequestBody(&conf, false), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "validate_request_body", "false"), + testAccCheckAWSAPIGatewayRequestValidatorValidateRequestParameters(&conf, false), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "validate_request_parameters", "false"), + ), + }, + resource.TestStep{ + Config: testAccAWSAPIGatewayRequestValidatorUpdatedConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayRequestValidatorExists("aws_api_gateway_request_validator.test", &conf), + testAccCheckAWSAPIGatewayRequestValidatorName(&conf, "tf-acc-test-request-validator_modified"), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "name", "tf-acc-test-request-validator_modified"), + testAccCheckAWSAPIGatewayRequestValidatorValidateRequestBody(&conf, true), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "validate_request_body", "true"), + testAccCheckAWSAPIGatewayRequestValidatorValidateRequestParameters(&conf, true), + resource.TestCheckResourceAttr("aws_api_gateway_request_validator.test", "validate_request_parameters", "true"), + ), + }, + }, + }) +} + +func testAccCheckAWSAPIGatewayRequestValidatorName(conf *apigateway.UpdateRequestValidatorOutput, expectedName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if conf.Name == nil { + return fmt.Errorf("Empty Name, expected: %q", expectedName) + } + if *conf.Name != expectedName { + return fmt.Errorf("Name didn't match. Expected: %q, Given: %q", expectedName, *conf.Name) + } + return nil + } +} + +func testAccCheckAWSAPIGatewayRequestValidatorValidateRequestBody(conf *apigateway.UpdateRequestValidatorOutput, expectedValue bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if conf.ValidateRequestBody == nil { + return fmt.Errorf("Empty ValidateRequestBody, expected: %t", expectedValue) + } + if *conf.ValidateRequestBody != expectedValue { + return fmt.Errorf("ValidateRequestBody didn't match. Expected: %t, Given: %t", expectedValue, *conf.ValidateRequestBody) + } + return nil + } +} + +func testAccCheckAWSAPIGatewayRequestValidatorValidateRequestParameters(conf *apigateway.UpdateRequestValidatorOutput, expectedValue bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + if conf.ValidateRequestParameters == nil { + return fmt.Errorf("Empty ValidateRequestParameters, expected: %t", expectedValue) + } + if *conf.ValidateRequestParameters != expectedValue { + return fmt.Errorf("ValidateRequestParameters didn't match. Expected: %t, Given: %t", expectedValue, *conf.ValidateRequestParameters) + } + return nil + } +} + +func testAccCheckAWSAPIGatewayRequestValidatorExists(n string, res *apigateway.UpdateRequestValidatorOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No API Request Validator ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).apigateway + + req := &apigateway.GetRequestValidatorInput{ + RequestValidatorId: aws.String(rs.Primary.ID), + RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), + } + describe, err := conn.GetRequestValidator(req) + if err != nil { + return err + } + + *res = *describe + + return nil + } +} + +func testAccCheckAWSAPIGatewayRequestValidatorDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).apigateway + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_api_gateway_request_validator" { + continue + } + + req := &apigateway.GetRequestValidatorInput{ + RequestValidatorId: aws.String(rs.Primary.ID), + RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), + } + _, err := conn.GetRequestValidator(req) + + if err == nil { + return fmt.Errorf("API Request Validator still exists") + } + + aws2err, ok := err.(awserr.Error) + if !ok { + return err + } + if aws2err.Code() != apigateway.ErrCodeNotFoundException { + return err + } + + return nil + } + + return nil +} + +const testAccAWSAPIGatewayRequestValidatorConfig_base = ` +resource "aws_api_gateway_rest_api" "test" { + name = "tf-request-validator-test" +} +` + +const testAccAWSAPIGatewayRequestValidatorConfig = testAccAWSAPIGatewayRequestValidatorConfig_base + ` +resource "aws_api_gateway_request_validator" "test" { + name = "tf-acc-test-request-validator" + rest_api_id = "${aws_api_gateway_rest_api.test.id}" +} +` + +const testAccAWSAPIGatewayRequestValidatorUpdatedConfig = testAccAWSAPIGatewayRequestValidatorConfig_base + ` +resource "aws_api_gateway_request_validator" "test" { + name = "tf-acc-test-request-validator_modified" + rest_api_id = "${aws_api_gateway_rest_api.test.id}" + validate_request_body = true + validate_request_parameters = true +} +`