Skip to content

Commit

Permalink
Merge pull request #18410 from ewbankkit/f-r/aws_apigatewayv2_route-a…
Browse files Browse the repository at this point in the history
…dd-request_parameters

r/aws apigatewayv2 route: Add `request_parameters` attribute
  • Loading branch information
breathingdust authored Mar 31, 2021
2 parents f81ce4d + 79598c7 commit 0a34535
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 77 deletions.
3 changes: 3 additions & 0 deletions .changelog/18410.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_apigatewayv2_route: Add `request_parameter` attribute
```
219 changes: 166 additions & 53 deletions aws/resource_aws_apigatewayv2_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -38,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,
Expand All @@ -66,6 +62,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,
Expand Down Expand Up @@ -107,6 +120,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))
}
Expand All @@ -117,7 +133,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))
Expand All @@ -132,25 +148,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)
Expand All @@ -162,45 +183,86 @@ 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{}))
}
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))
var requestParameters map[string]*apigatewayv2.ParameterConstraints

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)
}
}
}

requestParameters = expandApiGatewayV2RouteRequestParameters(ns.List())
}

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)
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))
}

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)
Expand All @@ -214,11 +276,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
Expand Down Expand Up @@ -252,3 +316,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
}
18 changes: 11 additions & 7 deletions aws/resource_aws_apigatewayv2_route_response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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", ""),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand All @@ -211,5 +215,5 @@ resource "aws_apigatewayv2_route_response" "test" {
"test" = aws_apigatewayv2_model.test.name
}
}
`
`)
}
Loading

0 comments on commit 0a34535

Please sign in to comment.