Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r/aws apigatewayv2 route: Add request_parameters attribute #18410

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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