From 6b40d9dd02d0d70a86253e57b1be911e90d08fc7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Mar 2021 11:55:42 -0400 Subject: [PATCH 1/4] r/aws_apigatewayv2_route: Add 'request_parameter' attribute. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccAWSAPIGatewayV2Route_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSAPIGatewayV2Route_ -timeout 180m === RUN TestAccAWSAPIGatewayV2Route_basic === PAUSE TestAccAWSAPIGatewayV2Route_basic === RUN TestAccAWSAPIGatewayV2Route_disappears === PAUSE TestAccAWSAPIGatewayV2Route_disappears === RUN TestAccAWSAPIGatewayV2Route_Authorizer === PAUSE TestAccAWSAPIGatewayV2Route_Authorizer === RUN TestAccAWSAPIGatewayV2Route_JwtAuthorization === PAUSE TestAccAWSAPIGatewayV2Route_JwtAuthorization === RUN TestAccAWSAPIGatewayV2Route_Model === PAUSE TestAccAWSAPIGatewayV2Route_Model === RUN TestAccAWSAPIGatewayV2Route_SimpleAttributes === PAUSE TestAccAWSAPIGatewayV2Route_SimpleAttributes === RUN TestAccAWSAPIGatewayV2Route_Target === PAUSE TestAccAWSAPIGatewayV2Route_Target === RUN TestAccAWSAPIGatewayV2Route_UpdateRouteKey === PAUSE TestAccAWSAPIGatewayV2Route_UpdateRouteKey === CONT TestAccAWSAPIGatewayV2Route_basic === CONT TestAccAWSAPIGatewayV2Route_SimpleAttributes === CONT TestAccAWSAPIGatewayV2Route_UpdateRouteKey === CONT TestAccAWSAPIGatewayV2Route_Target === CONT TestAccAWSAPIGatewayV2Route_JwtAuthorization === CONT TestAccAWSAPIGatewayV2Route_Model === CONT TestAccAWSAPIGatewayV2Route_Authorizer === CONT TestAccAWSAPIGatewayV2Route_disappears --- PASS: TestAccAWSAPIGatewayV2Route_disappears (26.14s) --- PASS: TestAccAWSAPIGatewayV2Route_basic (29.35s) --- PASS: TestAccAWSAPIGatewayV2Route_Model (31.10s) --- PASS: TestAccAWSAPIGatewayV2Route_Target (31.49s) --- PASS: TestAccAWSAPIGatewayV2Route_UpdateRouteKey (41.19s) --- PASS: TestAccAWSAPIGatewayV2Route_SimpleAttributes (51.22s) --- PASS: TestAccAWSAPIGatewayV2Route_Authorizer (69.92s) --- PASS: TestAccAWSAPIGatewayV2Route_JwtAuthorization (75.50s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 75.587s --- aws/resource_aws_apigatewayv2_route.go | 126 ++++++++++++++++-- aws/resource_aws_apigatewayv2_route_test.go | 13 +- .../docs/r/apigatewayv2_route.html.markdown | 6 + 3 files changed, 136 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_apigatewayv2_route.go b/aws/resource_aws_apigatewayv2_route.go index 4ddb0537b92..78b53047cc7 100644 --- a/aws/resource_aws_apigatewayv2_route.go +++ b/aws/resource_aws_apigatewayv2_route.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/apigatewayv2" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) @@ -66,6 +67,23 @@ func resourceAwsApiGatewayV2Route() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "request_parameter": { + Type: schema.TypeSet, + Optional: true, + MinItems: 0, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "request_parameter_key": { + Type: schema.TypeString, + Required: true, + }, + "required": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, "route_key": { Type: schema.TypeString, Required: true, @@ -107,6 +125,9 @@ func resourceAwsApiGatewayV2RouteCreate(d *schema.ResourceData, meta interface{} if v, ok := d.GetOk("request_models"); ok { req.RequestModels = stringMapToPointers(v.(map[string]interface{})) } + if v, ok := d.GetOk("request_parameter"); ok && v.(*schema.Set).Len() > 0 { + req.RequestParameters = expandApiGatewayV2RouteRequestParameters(v.(*schema.Set).List()) + } if v, ok := d.GetOk("route_response_selection_expression"); ok { req.RouteResponseSelectionExpression = aws.String(v.(string)) } @@ -117,7 +138,7 @@ func resourceAwsApiGatewayV2RouteCreate(d *schema.ResourceData, meta interface{} log.Printf("[DEBUG] Creating API Gateway v2 route: %s", req) resp, err := conn.CreateRoute(req) if err != nil { - return fmt.Errorf("error creating API Gateway v2 route: %s", err) + return fmt.Errorf("error creating API Gateway v2 route: %w", err) } d.SetId(aws.StringValue(resp.RouteId)) @@ -132,25 +153,30 @@ func resourceAwsApiGatewayV2RouteRead(d *schema.ResourceData, meta interface{}) ApiId: aws.String(d.Get("api_id").(string)), RouteId: aws.String(d.Id()), }) - if isAWSErr(err, apigatewayv2.ErrCodeNotFoundException, "") { + + if tfawserr.ErrCodeEquals(err, apigatewayv2.ErrCodeNotFoundException) { log.Printf("[WARN] API Gateway v2 route (%s) not found, removing from state", d.Id()) d.SetId("") return nil } + if err != nil { - return fmt.Errorf("error reading API Gateway v2 route: %s", err) + return fmt.Errorf("error reading API Gateway v2 route (%s): %w", d.Id(), err) } d.Set("api_key_required", resp.ApiKeyRequired) if err := d.Set("authorization_scopes", flattenStringSet(resp.AuthorizationScopes)); err != nil { - return fmt.Errorf("error setting authorization_scopes: %s", err) + return fmt.Errorf("error setting authorization_scopes: %w", err) } d.Set("authorization_type", resp.AuthorizationType) d.Set("authorizer_id", resp.AuthorizerId) d.Set("model_selection_expression", resp.ModelSelectionExpression) d.Set("operation_name", resp.OperationName) if err := d.Set("request_models", pointersMapToStringList(resp.RequestModels)); err != nil { - return fmt.Errorf("error setting request_models: %s", err) + return fmt.Errorf("error setting request_models: %w", err) + } + if err := d.Set("request_parameter", flattenApiGatewayV2RouteRequestParameters(resp.RequestParameters)); err != nil { + return fmt.Errorf("error setting request_parameter: %w", err) } d.Set("route_key", resp.RouteKey) d.Set("route_response_selection_expression", resp.RouteResponseSelectionExpression) @@ -187,6 +213,38 @@ func resourceAwsApiGatewayV2RouteUpdate(d *schema.ResourceData, meta interface{} if d.HasChange("request_models") { req.RequestModels = stringMapToPointers(d.Get("request_models").(map[string]interface{})) } + if d.HasChange("request_parameter") { + o, n := d.GetChange("request_parameter") + os := o.(*schema.Set) + ns := n.(*schema.Set) + + for _, tfMapRaw := range os.Difference(ns).List() { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + if v, ok := tfMap["request_parameter_key"].(string); ok && v != "" { + log.Printf("[DEBUG] Deleting API Gateway v2 route (%s) request parameter (%s)", d.Id(), v) + _, err := conn.DeleteRouteRequestParameter(&apigatewayv2.DeleteRouteRequestParameterInput{ + ApiId: aws.String(d.Get("api_id").(string)), + RequestParameterKey: aws.String(v), + RouteId: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, apigatewayv2.ErrCodeNotFoundException) { + continue + } + + if err != nil { + return fmt.Errorf("error deleting API Gateway v2 route (%s) request parameter (%s): %w", d.Id(), v, err) + } + } + } + + req.RequestParameters = expandApiGatewayV2RouteRequestParameters(ns.List()) + } if d.HasChange("route_key") { req.RouteKey = aws.String(d.Get("route_key").(string)) } @@ -199,8 +257,9 @@ func resourceAwsApiGatewayV2RouteUpdate(d *schema.ResourceData, meta interface{} log.Printf("[DEBUG] Updating API Gateway v2 route: %s", req) _, err := conn.UpdateRoute(req) + if err != nil { - return fmt.Errorf("error updating API Gateway v2 route: %s", err) + return fmt.Errorf("error updating API Gateway v2 route (%s): %w", d.Id(), err) } return resourceAwsApiGatewayV2RouteRead(d, meta) @@ -214,11 +273,13 @@ func resourceAwsApiGatewayV2RouteDelete(d *schema.ResourceData, meta interface{} ApiId: aws.String(d.Get("api_id").(string)), RouteId: aws.String(d.Id()), }) - if isAWSErr(err, apigatewayv2.ErrCodeNotFoundException, "") { + + if tfawserr.ErrCodeEquals(err, apigatewayv2.ErrCodeNotFoundException) { return nil } + if err != nil { - return fmt.Errorf("error deleting API Gateway v2 route: %s", err) + return fmt.Errorf("error deleting API Gateway v2 route (%s): %w", d.Id(), err) } return nil @@ -252,3 +313,52 @@ func resourceAwsApiGatewayV2RouteImport(d *schema.ResourceData, meta interface{} return []*schema.ResourceData{d}, nil } + +func expandApiGatewayV2RouteRequestParameters(tfList []interface{}) map[string]*apigatewayv2.ParameterConstraints { + if len(tfList) == 0 { + return nil + } + + apiObjects := map[string]*apigatewayv2.ParameterConstraints{} + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := &apigatewayv2.ParameterConstraints{} + + if v, ok := tfMap["required"].(bool); ok { + apiObject.Required = aws.Bool(v) + } + + if v, ok := tfMap["request_parameter_key"].(string); ok && v != "" { + apiObjects[v] = apiObject + } + } + + return apiObjects +} + +func flattenApiGatewayV2RouteRequestParameters(apiObjects map[string]*apigatewayv2.ParameterConstraints) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for k, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, map[string]interface{}{ + "request_parameter_key": k, + "required": aws.BoolValue(apiObject.Required), + }) + } + + return tfList +} diff --git a/aws/resource_aws_apigatewayv2_route_test.go b/aws/resource_aws_apigatewayv2_route_test.go index 9fdf09aa95a..fca5ab309b5 100644 --- a/aws/resource_aws_apigatewayv2_route_test.go +++ b/aws/resource_aws_apigatewayv2_route_test.go @@ -34,6 +34,7 @@ func TestAccAWSAPIGatewayV2Route_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -97,6 +98,7 @@ func TestAccAWSAPIGatewayV2Route_Authorizer(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$connect"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -119,6 +121,7 @@ func TestAccAWSAPIGatewayV2Route_Authorizer(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$connect"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -152,6 +155,7 @@ func TestAccAWSAPIGatewayV2Route_JwtAuthorization(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "GET /test"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -174,6 +178,7 @@ func TestAccAWSAPIGatewayV2Route_JwtAuthorization(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "GET /test"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -208,6 +213,7 @@ func TestAccAWSAPIGatewayV2Route_Model(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "1"), resource.TestCheckResourceAttrPair(resourceName, "request_models.test", modelResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -245,6 +251,7 @@ func TestAccAWSAPIGatewayV2Route_SimpleAttributes(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", "GET"), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", "$default"), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -260,7 +267,7 @@ func TestAccAWSAPIGatewayV2Route_SimpleAttributes(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), - resource.TestCheckResourceAttr(resourceName, "request_parameters.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -276,6 +283,7 @@ func TestAccAWSAPIGatewayV2Route_SimpleAttributes(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", "GET"), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", "$default"), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -314,6 +322,7 @@ func TestAccAWSAPIGatewayV2Route_Target(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "$default"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), testAccCheckAWSAPIGatewayV2RouteTarget(resourceName, integrationResourceName), @@ -351,6 +360,7 @@ func TestAccAWSAPIGatewayV2Route_UpdateRouteKey(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "GET /path"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), @@ -366,6 +376,7 @@ func TestAccAWSAPIGatewayV2Route_UpdateRouteKey(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "operation_name", ""), resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), resource.TestCheckResourceAttr(resourceName, "route_key", "POST /new/path"), resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), resource.TestCheckResourceAttr(resourceName, "target", ""), diff --git a/website/docs/r/apigatewayv2_route.html.markdown b/website/docs/r/apigatewayv2_route.html.markdown index 11d9a7463d2..c491f0da86f 100644 --- a/website/docs/r/apigatewayv2_route.html.markdown +++ b/website/docs/r/apigatewayv2_route.html.markdown @@ -68,9 +68,15 @@ Defaults to `NONE`. * `model_selection_expression` - (Optional) The [model selection expression](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html#apigateway-websocket-api-model-selection-expressions) for the route. Supported only for WebSocket APIs. * `operation_name` - (Optional) The operation name for the route. Must be between 1 and 64 characters in length. * `request_models` - (Optional) The request models for the route. Supported only for WebSocket APIs. +* `request_parameter` - (Optional) The request parameters for the route. Supported only for WebSocket APIs. * `route_response_selection_expression` - (Optional) The [route response selection expression](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html#apigateway-websocket-api-route-response-selection-expressions) for the route. Supported only for WebSocket APIs. * `target` - (Optional) The target for the route, of the form `integrations/`*`IntegrationID`*, where *`IntegrationID`* is the identifier of an [`aws_apigatewayv2_integration`](apigatewayv2_integration.html) resource. +The `request_parameter` object supports the following: + +* `request_parameter_key` - (Required) Request parameter key. This is a [request data mapping parameter](https://docs.aws.amazon.com/apigateway/latest/developerguide/websocket-api-data-mapping.html#websocket-mapping-request-parameters). +* `required` - (Required) Boolean whether or not the parameter is required. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: From 1ef6a5574a9dea0f3058d36b7e1fecf4ef3b290f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Mar 2021 11:59:56 -0400 Subject: [PATCH 2/4] Use apigatewayv2.AuthorizationType_Values (#14601). --- aws/resource_aws_apigatewayv2_route.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_apigatewayv2_route.go b/aws/resource_aws_apigatewayv2_route.go index 78b53047cc7..a5a82c3568a 100644 --- a/aws/resource_aws_apigatewayv2_route.go +++ b/aws/resource_aws_apigatewayv2_route.go @@ -39,15 +39,10 @@ func resourceAwsApiGatewayV2Route() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, }, "authorization_type": { - Type: schema.TypeString, - Optional: true, - Default: apigatewayv2.AuthorizationTypeNone, - ValidateFunc: validation.StringInSlice([]string{ - apigatewayv2.AuthorizationTypeNone, - apigatewayv2.AuthorizationTypeAwsIam, - apigatewayv2.AuthorizationTypeCustom, - apigatewayv2.AuthorizationTypeJwt, - }, false), + Type: schema.TypeString, + Optional: true, + Default: apigatewayv2.AuthorizationTypeNone, + ValidateFunc: validation.StringInSlice(apigatewayv2.AuthorizationType_Values(), false), }, "authorizer_id": { Type: schema.TypeString, From 94ed438f64a80c23d4d06ec23f2062f22337857b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Mar 2021 13:13:44 -0400 Subject: [PATCH 3/4] r/aws_apigatewayv2_route: Add 'request_parameter' acceptance tests. Acceptance test output: $ make testacc TEST=./aws/ TESTARGS='-run=TestAccAWSAPIGatewayV2Route_' ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./aws -v -count 1 -parallel 20 -run=TestAccAWSAPIGatewayV2Route_ -timeout 180m === RUN TestAccAWSAPIGatewayV2Route_basic === PAUSE TestAccAWSAPIGatewayV2Route_basic === RUN TestAccAWSAPIGatewayV2Route_disappears === PAUSE TestAccAWSAPIGatewayV2Route_disappears === RUN TestAccAWSAPIGatewayV2Route_Authorizer === PAUSE TestAccAWSAPIGatewayV2Route_Authorizer === RUN TestAccAWSAPIGatewayV2Route_JwtAuthorization === PAUSE TestAccAWSAPIGatewayV2Route_JwtAuthorization === RUN TestAccAWSAPIGatewayV2Route_Model === PAUSE TestAccAWSAPIGatewayV2Route_Model === RUN TestAccAWSAPIGatewayV2Route_RequestParameters === PAUSE TestAccAWSAPIGatewayV2Route_RequestParameters === RUN TestAccAWSAPIGatewayV2Route_SimpleAttributes === PAUSE TestAccAWSAPIGatewayV2Route_SimpleAttributes === RUN TestAccAWSAPIGatewayV2Route_Target === PAUSE TestAccAWSAPIGatewayV2Route_Target === RUN TestAccAWSAPIGatewayV2Route_UpdateRouteKey === PAUSE TestAccAWSAPIGatewayV2Route_UpdateRouteKey === CONT TestAccAWSAPIGatewayV2Route_basic === CONT TestAccAWSAPIGatewayV2Route_RequestParameters === CONT TestAccAWSAPIGatewayV2Route_UpdateRouteKey === CONT TestAccAWSAPIGatewayV2Route_Target === CONT TestAccAWSAPIGatewayV2Route_SimpleAttributes === CONT TestAccAWSAPIGatewayV2Route_JwtAuthorization === CONT TestAccAWSAPIGatewayV2Route_Model === CONT TestAccAWSAPIGatewayV2Route_Authorizer === CONT TestAccAWSAPIGatewayV2Route_disappears --- PASS: TestAccAWSAPIGatewayV2Route_disappears (27.57s) --- PASS: TestAccAWSAPIGatewayV2Route_basic (31.98s) --- PASS: TestAccAWSAPIGatewayV2Route_Model (32.60s) --- PASS: TestAccAWSAPIGatewayV2Route_Target (32.61s) --- PASS: TestAccAWSAPIGatewayV2Route_UpdateRouteKey (41.37s) --- PASS: TestAccAWSAPIGatewayV2Route_RequestParameters (56.66s) --- PASS: TestAccAWSAPIGatewayV2Route_Authorizer (72.94s) --- PASS: TestAccAWSAPIGatewayV2Route_JwtAuthorization (79.44s) --- PASS: TestAccAWSAPIGatewayV2Route_SimpleAttributes (82.78s) PASS ok github.com/terraform-providers/terraform-provider-aws/aws 82.905s --- aws/resource_aws_apigatewayv2_route.go | 86 +++++---- ...ce_aws_apigatewayv2_route_response_test.go | 18 +- aws/resource_aws_apigatewayv2_route_test.go | 174 ++++++++++++++++-- 3 files changed, 216 insertions(+), 62 deletions(-) diff --git a/aws/resource_aws_apigatewayv2_route.go b/aws/resource_aws_apigatewayv2_route.go index a5a82c3568a..b4811981232 100644 --- a/aws/resource_aws_apigatewayv2_route.go +++ b/aws/resource_aws_apigatewayv2_route.go @@ -183,31 +183,8 @@ func resourceAwsApiGatewayV2RouteRead(d *schema.ResourceData, meta interface{}) func resourceAwsApiGatewayV2RouteUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).apigatewayv2conn - req := &apigatewayv2.UpdateRouteInput{ - ApiId: aws.String(d.Get("api_id").(string)), - RouteId: aws.String(d.Id()), - } - if d.HasChange("api_key_required") { - req.ApiKeyRequired = aws.Bool(d.Get("api_key_required").(bool)) - } - if d.HasChange("authorization_scopes") { - req.AuthorizationScopes = expandStringSet(d.Get("authorization_scopes").(*schema.Set)) - } - if d.HasChange("authorization_type") { - req.AuthorizationType = aws.String(d.Get("authorization_type").(string)) - } - if d.HasChange("authorizer_id") { - req.AuthorizerId = aws.String(d.Get("authorizer_id").(string)) - } - if d.HasChange("model_selection_expression") { - req.ModelSelectionExpression = aws.String(d.Get("model_selection_expression").(string)) - } - if d.HasChange("operation_name") { - req.OperationName = aws.String(d.Get("operation_name").(string)) - } - if d.HasChange("request_models") { - req.RequestModels = stringMapToPointers(d.Get("request_models").(map[string]interface{})) - } + var requestParameters map[string]*apigatewayv2.ParameterConstraints + if d.HasChange("request_parameter") { o, n := d.GetChange("request_parameter") os := o.(*schema.Set) @@ -238,23 +215,54 @@ func resourceAwsApiGatewayV2RouteUpdate(d *schema.ResourceData, meta interface{} } } - req.RequestParameters = expandApiGatewayV2RouteRequestParameters(ns.List()) - } - if d.HasChange("route_key") { - req.RouteKey = aws.String(d.Get("route_key").(string)) - } - if d.HasChange("route_response_selection_expression") { - req.RouteResponseSelectionExpression = aws.String(d.Get("route_response_selection_expression").(string)) - } - if d.HasChange("target") { - req.Target = aws.String(d.Get("target").(string)) + requestParameters = expandApiGatewayV2RouteRequestParameters(ns.List()) } - log.Printf("[DEBUG] Updating API Gateway v2 route: %s", req) - _, err := conn.UpdateRoute(req) + if d.HasChangesExcept("request_parameter") || len(requestParameters) > 0 { + req := &apigatewayv2.UpdateRouteInput{ + ApiId: aws.String(d.Get("api_id").(string)), + RouteId: aws.String(d.Id()), + } + if d.HasChange("api_key_required") { + req.ApiKeyRequired = aws.Bool(d.Get("api_key_required").(bool)) + } + if d.HasChange("authorization_scopes") { + req.AuthorizationScopes = expandStringSet(d.Get("authorization_scopes").(*schema.Set)) + } + if d.HasChange("authorization_type") { + req.AuthorizationType = aws.String(d.Get("authorization_type").(string)) + } + if d.HasChange("authorizer_id") { + req.AuthorizerId = aws.String(d.Get("authorizer_id").(string)) + } + if d.HasChange("model_selection_expression") { + req.ModelSelectionExpression = aws.String(d.Get("model_selection_expression").(string)) + } + if d.HasChange("operation_name") { + req.OperationName = aws.String(d.Get("operation_name").(string)) + } + if d.HasChange("request_models") { + req.RequestModels = stringMapToPointers(d.Get("request_models").(map[string]interface{})) + } + if d.HasChange("request_parameter") { + req.RequestParameters = requestParameters + } + if d.HasChange("route_key") { + req.RouteKey = aws.String(d.Get("route_key").(string)) + } + if d.HasChange("route_response_selection_expression") { + req.RouteResponseSelectionExpression = aws.String(d.Get("route_response_selection_expression").(string)) + } + if d.HasChange("target") { + req.Target = aws.String(d.Get("target").(string)) + } - if err != nil { - return fmt.Errorf("error updating API Gateway v2 route (%s): %w", d.Id(), err) + log.Printf("[DEBUG] Updating API Gateway v2 route: %s", req) + _, err := conn.UpdateRoute(req) + + if err != nil { + return fmt.Errorf("error updating API Gateway v2 route (%s): %w", d.Id(), err) + } } return resourceAwsApiGatewayV2RouteRead(d, meta) diff --git a/aws/resource_aws_apigatewayv2_route_response_test.go b/aws/resource_aws_apigatewayv2_route_response_test.go index d2f47aea3b0..6a843c1b205 100644 --- a/aws/resource_aws_apigatewayv2_route_response_test.go +++ b/aws/resource_aws_apigatewayv2_route_response_test.go @@ -26,7 +26,7 @@ func TestAccAWSAPIGatewayV2RouteResponse_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAPIGatewayV2RouteResponseDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayV2RouteResponseConfig_basic(rName), + Config: testAccAWSAPIGatewayV2RouteResponseConfig_basicWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2RouteResponseExists(resourceName, &apiId, &routeId, &v), resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), @@ -58,7 +58,7 @@ func TestAccAWSAPIGatewayV2RouteResponse_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSAPIGatewayV2RouteResponseDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayV2RouteResponseConfig_basic(rName), + Config: testAccAWSAPIGatewayV2RouteResponseConfig_basicWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2RouteResponseExists(resourceName, &apiId, &routeId, &v), testAccCheckAWSAPIGatewayV2RouteResponseDisappears(&apiId, &routeId, &v), @@ -188,18 +188,22 @@ func testAccAWSAPIGatewayV2RouteResponseImportStateIdFunc(resourceName string) r } } -func testAccAWSAPIGatewayV2RouteResponseConfig_basic(rName string) string { - return testAccAWSAPIGatewayV2RouteConfig_basic(rName) + ` +func testAccAWSAPIGatewayV2RouteResponseConfig_basicWebSocket(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_basicWebSocket(rName), + ` resource "aws_apigatewayv2_route_response" "test" { api_id = aws_apigatewayv2_api.test.id route_id = aws_apigatewayv2_route.test.id route_response_key = "$default" } -` +`) } func testAccAWSAPIGatewayV2RouteResponseConfig_model(rName string) string { - return testAccAWSAPIGatewayV2RouteConfig_model(rName) + ` + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_model(rName), + ` resource "aws_apigatewayv2_route_response" "test" { api_id = aws_apigatewayv2_api.test.id route_id = aws_apigatewayv2_route.test.id @@ -211,5 +215,5 @@ resource "aws_apigatewayv2_route_response" "test" { "test" = aws_apigatewayv2_model.test.name } } -` +`) } diff --git a/aws/resource_aws_apigatewayv2_route_test.go b/aws/resource_aws_apigatewayv2_route_test.go index fca5ab309b5..4bea408bc1c 100644 --- a/aws/resource_aws_apigatewayv2_route_test.go +++ b/aws/resource_aws_apigatewayv2_route_test.go @@ -25,7 +25,7 @@ func TestAccAWSAPIGatewayV2Route_basic(t *testing.T) { CheckDestroy: testAccCheckAWSAPIGatewayV2RouteDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayV2RouteConfig_basic(rName), + Config: testAccAWSAPIGatewayV2RouteConfig_basicWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), resource.TestCheckResourceAttr(resourceName, "api_key_required", "false"), @@ -63,7 +63,7 @@ func TestAccAWSAPIGatewayV2Route_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSAPIGatewayV2RouteDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSAPIGatewayV2RouteConfig_basic(rName), + Config: testAccAWSAPIGatewayV2RouteConfig_basicWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), testAccCheckResourceDisappears(testAccProvider, resourceAwsApiGatewayV2Route(), resourceName), @@ -229,6 +229,88 @@ func TestAccAWSAPIGatewayV2Route_Model(t *testing.T) { }) } +func TestAccAWSAPIGatewayV2Route_RequestParameters(t *testing.T) { + var apiId string + var v apigatewayv2.GetRouteOutput + resourceName := "aws_apigatewayv2_route.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, apigatewayv2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayV2RouteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAPIGatewayV2RouteConfig_requestParameters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), + resource.TestCheckResourceAttr(resourceName, "api_key_required", "false"), + resource.TestCheckResourceAttr(resourceName, "authorization_type", apigatewayv2.AuthorizationTypeNone), + resource.TestCheckResourceAttr(resourceName, "authorizer_id", ""), + resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "operation_name", ""), + resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "request_parameter.*", map[string]string{ + "request_parameter_key": "route.request.header.authorization", + "required": "true", + }), + resource.TestCheckResourceAttr(resourceName, "route_key", "$connect"), + resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "target", ""), + ), + }, + { + Config: testAccAWSAPIGatewayV2RouteConfig_requestParametersUpdated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), + resource.TestCheckResourceAttr(resourceName, "api_key_required", "false"), + resource.TestCheckResourceAttr(resourceName, "authorization_type", apigatewayv2.AuthorizationTypeNone), + resource.TestCheckResourceAttr(resourceName, "authorizer_id", ""), + resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "operation_name", ""), + resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "request_parameter.*", map[string]string{ + "request_parameter_key": "route.request.header.authorization", + "required": "false", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "request_parameter.*", map[string]string{ + "request_parameter_key": "route.request.querystring.authToken", + "required": "true", + }), + resource.TestCheckResourceAttr(resourceName, "route_key", "$connect"), + resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "target", ""), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccAWSAPIGatewayV2RouteImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSAPIGatewayV2RouteConfig_noRequestParameters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), + resource.TestCheckResourceAttr(resourceName, "api_key_required", "false"), + resource.TestCheckResourceAttr(resourceName, "authorization_type", apigatewayv2.AuthorizationTypeNone), + resource.TestCheckResourceAttr(resourceName, "authorizer_id", ""), + resource.TestCheckResourceAttr(resourceName, "model_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "operation_name", ""), + resource.TestCheckResourceAttr(resourceName, "request_models.%", "0"), + resource.TestCheckResourceAttr(resourceName, "request_parameter.#", "0"), + resource.TestCheckResourceAttr(resourceName, "route_key", "$connect"), + resource.TestCheckResourceAttr(resourceName, "route_response_selection_expression", ""), + resource.TestCheckResourceAttr(resourceName, "target", ""), + ), + }, + }, + }) +} + func TestAccAWSAPIGatewayV2Route_SimpleAttributes(t *testing.T) { var apiId string var v apigatewayv2.GetRouteOutput @@ -258,7 +340,7 @@ func TestAccAWSAPIGatewayV2Route_SimpleAttributes(t *testing.T) { ), }, { - Config: testAccAWSAPIGatewayV2RouteConfig_basic(rName), + Config: testAccAWSAPIGatewayV2RouteConfig_basicWebSocket(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayV2RouteExists(resourceName, &apiId, &v), resource.TestCheckResourceAttr(resourceName, "api_key_required", "false"), @@ -487,17 +569,21 @@ resource "aws_apigatewayv2_api" "test" { `, rName) } -func testAccAWSAPIGatewayV2RouteConfig_basic(rName string) string { - return testAccAWSAPIGatewayV2RouteConfig_apiWebSocket(rName) + ` +func testAccAWSAPIGatewayV2RouteConfig_basicWebSocket(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_apiWebSocket(rName), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "$default" } -` +`) } func testAccAWSAPIGatewayV2RouteConfig_authorizer(rName string) string { - return testAccAWSAPIGatewayV2AuthorizerConfig_basic(rName) + ` + return composeConfig( + testAccAWSAPIGatewayV2AuthorizerConfig_basic(rName), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "$connect" @@ -505,22 +591,26 @@ resource "aws_apigatewayv2_route" "test" { authorization_type = "CUSTOM" authorizer_id = aws_apigatewayv2_authorizer.test.id } -` +`) } func testAccAWSAPIGatewayV2RouteConfig_authorizerUpdated(rName string) string { - return testAccAWSAPIGatewayV2AuthorizerConfig_basic(rName) + ` + return composeConfig( + testAccAWSAPIGatewayV2AuthorizerConfig_basic(rName), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "$connect" authorization_type = "AWS_IAM" } -` +`) } func testAccAWSAPIGatewayV2RouteConfig_jwtAuthorization(rName string) string { - return testAccAWSAPIGatewayV2AuthorizerConfig_jwt(rName) + ` + return composeConfig( + testAccAWSAPIGatewayV2AuthorizerConfig_jwt(rName), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "GET /test" @@ -530,11 +620,13 @@ resource "aws_apigatewayv2_route" "test" { authorization_scopes = ["user.id", "user.email"] } -` +`) } func testAccAWSAPIGatewayV2RouteConfig_jwtAuthorizationUpdated(rName string) string { - return testAccAWSAPIGatewayV2AuthorizerConfig_jwt(rName) + ` + return composeConfig( + testAccAWSAPIGatewayV2AuthorizerConfig_jwt(rName), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "GET /test" @@ -544,7 +636,7 @@ resource "aws_apigatewayv2_route" "test" { authorization_scopes = ["user.email"] } -` +`) } func testAccAWSAPIGatewayV2RouteConfig_model(rName string) string { @@ -561,7 +653,9 @@ func testAccAWSAPIGatewayV2RouteConfig_model(rName string) string { } ` - return testAccAWSAPIGatewayV2ModelConfig_basic(rName, schema) + ` + return composeConfig( + testAccAWSAPIGatewayV2ModelConfig_basic(rName, schema), + ` resource "aws_apigatewayv2_route" "test" { api_id = aws_apigatewayv2_api.test.id route_key = "$default" @@ -572,7 +666,55 @@ resource "aws_apigatewayv2_route" "test" { "test" = aws_apigatewayv2_model.test.name } } -` +`) +} + +func testAccAWSAPIGatewayV2RouteConfig_noRequestParameters(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_apiWebSocket(rName), + ` +resource "aws_apigatewayv2_route" "test" { + api_id = aws_apigatewayv2_api.test.id + route_key = "$connect" +} +`) +} + +func testAccAWSAPIGatewayV2RouteConfig_requestParameters(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_apiWebSocket(rName), + ` +resource "aws_apigatewayv2_route" "test" { + api_id = aws_apigatewayv2_api.test.id + route_key = "$connect" + + request_parameter { + request_parameter_key = "route.request.header.authorization" + required = true + } +} +`) +} + +func testAccAWSAPIGatewayV2RouteConfig_requestParametersUpdated(rName string) string { + return composeConfig( + testAccAWSAPIGatewayV2RouteConfig_apiWebSocket(rName), + ` +resource "aws_apigatewayv2_route" "test" { + api_id = aws_apigatewayv2_api.test.id + route_key = "$connect" + + request_parameter { + request_parameter_key = "route.request.header.authorization" + required = false + } + + request_parameter { + request_parameter_key = "route.request.querystring.authToken" + required = true + } +} +`) } func testAccAWSAPIGatewayV2RouteConfig_routeKey(rName, routeKey string) string { From 79598c7c26115d8cf4c113fe4c112d6e7e8941f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 25 Mar 2021 13:23:49 -0400 Subject: [PATCH 4/4] Add CHANGELOG entry. --- .changelog/18410.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/18410.txt diff --git a/.changelog/18410.txt b/.changelog/18410.txt new file mode 100644 index 00000000000..a15b77ed2c2 --- /dev/null +++ b/.changelog/18410.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_apigatewayv2_route: Add `request_parameter` attribute +```