provider/aws: add response parameters support to api gateway (#6344)

* provider/aws: Add support for response parameters aws_api_gateway_integration_response and aws_api_gateway_method response.

* fix spacing

* fix spacing

* gofmt

* add update test; add docs; add reimplement TODO; add field read

* resolve conflict

* fix expandAPIGatewayMethodResponse error handling
This commit is contained in:
Josh Taylor 2016-05-04 03:56:18 -07:00 committed by Radek Simko
parent 952152280e
commit 983da213af
7 changed files with 296 additions and 5 deletions

View File

@ -1,6 +1,7 @@
package aws package aws
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -54,6 +55,11 @@ func resourceAwsApiGatewayIntegrationResponse() *schema.Resource {
Optional: true, Optional: true,
Elem: schema.TypeString, Elem: schema.TypeString,
}, },
"response_parameters_in_json": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
}, },
} }
} }
@ -66,14 +72,21 @@ func resourceAwsApiGatewayIntegrationResponseCreate(d *schema.ResourceData, meta
templates[k] = v.(string) templates[k] = v.(string)
} }
parameters := make(map[string]string)
if v, ok := d.GetOk("response_parameters_in_json"); ok {
if err := json.Unmarshal([]byte(v.(string)), &parameters); err != nil {
return fmt.Errorf("Error unmarshaling request_parameters_in_json: %s", err)
}
}
input := apigateway.PutIntegrationResponseInput{ input := apigateway.PutIntegrationResponseInput{
HttpMethod: aws.String(d.Get("http_method").(string)), HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)), ResourceId: aws.String(d.Get("resource_id").(string)),
RestApiId: aws.String(d.Get("rest_api_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)),
StatusCode: aws.String(d.Get("status_code").(string)), StatusCode: aws.String(d.Get("status_code").(string)),
ResponseTemplates: aws.StringMap(templates), ResponseTemplates: aws.StringMap(templates),
// TODO implement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented // TODO reimplement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: nil, ResponseParameters: aws.StringMap(parameters),
} }
if v, ok := d.GetOk("selection_pattern"); ok { if v, ok := d.GetOk("selection_pattern"); ok {
input.SelectionPattern = aws.String(v.(string)) input.SelectionPattern = aws.String(v.(string))
@ -106,12 +119,13 @@ func resourceAwsApiGatewayIntegrationResponseRead(d *schema.ResourceData, meta i
} }
return err return err
} }
log.Printf("[DEBUG] Received API Gateway Integration Response: %s", integrationResponse) log.Printf("[DEBUG] Received API Gateway Integration Response: %s", integrationResponse)
d.SetId(fmt.Sprintf("agir-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string))) d.SetId(fmt.Sprintf("agir-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string)))
d.Set("response_templates", integrationResponse.ResponseTemplates) d.Set("response_templates", integrationResponse.ResponseTemplates)
d.Set("selection_pattern", integrationResponse.SelectionPattern) d.Set("selection_pattern", integrationResponse.SelectionPattern)
d.Set("response_parameters_in_json", aws.StringValueMap(integrationResponse.ResponseParameters))
return nil return nil
} }

View File

@ -30,6 +30,18 @@ func TestAccAWSAPIGatewayIntegrationResponse_basic(t *testing.T) {
"aws_api_gateway_integration_response.test", "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), "aws_api_gateway_integration_response.test", "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
), ),
}, },
resource.TestStep{
Config: testAccAWSAPIGatewayIntegrationResponseConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayIntegrationResponseExists("aws_api_gateway_integration_response.test", &conf),
testAccCheckAWSAPIGatewayIntegrationResponseAttributesUpdate(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration_response.test", "response_templates.application/json", "$input.path('$')"),
resource.TestCheckResourceAttr(
"aws_api_gateway_integration_response.test", "response_templates.application/xml", ""),
),
},
}, },
}) })
} }
@ -48,6 +60,31 @@ func testAccCheckAWSAPIGatewayIntegrationResponseAttributes(conf *apigateway.Int
if conf.SelectionPattern == nil || *conf.SelectionPattern != ".*" { if conf.SelectionPattern == nil || *conf.SelectionPattern != ".*" {
return fmt.Errorf("wrong SelectionPattern (expected .*)") return fmt.Errorf("wrong SelectionPattern (expected .*)")
} }
if *conf.ResponseParameters["method.response.header.Content-Type"] != "integration.response.body.type" {
return fmt.Errorf("wrong ResponseParameters for header.Content-Type")
}
return nil
}
}
func testAccCheckAWSAPIGatewayIntegrationResponseAttributesUpdate(conf *apigateway.IntegrationResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *conf.StatusCode != "400" {
return fmt.Errorf("wrong StatusCode: %q", *conf.StatusCode)
}
if *conf.ResponseTemplates["application/json"] != "$input.path('$')" {
return fmt.Errorf("wrong ResponseTemplate for application/json")
}
if conf.ResponseTemplates["application/xml"] != nil {
return fmt.Errorf("wrong ResponseTemplate for application/xml")
}
if conf.SelectionPattern != nil {
return fmt.Errorf("wrong SelectionPattern (expected nil)")
}
if conf.ResponseParameters["method.response.header.Content-Type"] != nil {
return fmt.Errorf("ResponseParameters for header.Content-Type shouldnt exist")
}
return nil return nil
} }
} }
@ -147,6 +184,12 @@ resource "aws_api_gateway_method_response" "error" {
response_models = { response_models = {
"application/json" = "Error" "application/json" = "Error"
} }
response_parameters_in_json = <<PARAMS
{
"method.response.header.Content-Type": true
}
PARAMS
} }
resource "aws_api_gateway_integration" "test" { resource "aws_api_gateway_integration" "test" {
@ -173,5 +216,77 @@ resource "aws_api_gateway_integration_response" "test" {
"application/json" = "" "application/json" = ""
"application/xml" = "#set($inputRoot = $input.path('$'))\n{ }" "application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
} }
response_parameters_in_json = <<PARAMS
{
"method.response.header.Content-Type": "integration.response.body.type"
}
PARAMS
}
`
const testAccAWSAPIGatewayIntegrationResponseConfigUpdate = `
resource "aws_api_gateway_rest_api" "test" {
name = "test"
}
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_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"
}
}
resource "aws_api_gateway_method_response" "error" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
status_code = "400"
response_models = {
"application/json" = "Error"
}
response_parameters_in_json = <<PARAMS
{
"method.response.header.Content-Type": true
}
PARAMS
}
resource "aws_api_gateway_integration" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
request_templates = {
"application/json" = ""
"application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
}
type = "MOCK"
}
resource "aws_api_gateway_integration_response" "test" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
status_code = "${aws_api_gateway_method_response.error.status_code}"
response_templates = {
"application/json" = "$input.path('$')"
"application/xml" = ""
}
} }
` `

View File

@ -1,6 +1,7 @@
package aws package aws
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"time" "time"
@ -49,6 +50,11 @@ func resourceAwsApiGatewayMethodResponse() *schema.Resource {
Optional: true, Optional: true,
Elem: schema.TypeString, Elem: schema.TypeString,
}, },
"response_parameters_in_json": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
}, },
} }
} }
@ -61,14 +67,21 @@ func resourceAwsApiGatewayMethodResponseCreate(d *schema.ResourceData, meta inte
models[k] = v.(string) models[k] = v.(string)
} }
parameters := make(map[string]bool)
if v, ok := d.GetOk("response_parameters_in_json"); ok {
if err := json.Unmarshal([]byte(v.(string)), &parameters); err != nil {
return fmt.Errorf("Error unmarshaling request_parameters_in_json: %s", err)
}
}
_, err := conn.PutMethodResponse(&apigateway.PutMethodResponseInput{ _, err := conn.PutMethodResponse(&apigateway.PutMethodResponseInput{
HttpMethod: aws.String(d.Get("http_method").(string)), HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)), ResourceId: aws.String(d.Get("resource_id").(string)),
RestApiId: aws.String(d.Get("rest_api_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)),
StatusCode: aws.String(d.Get("status_code").(string)), StatusCode: aws.String(d.Get("status_code").(string)),
ResponseModels: aws.StringMap(models), ResponseModels: aws.StringMap(models),
// TODO implement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented // TODO reimplement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: nil, ResponseParameters: aws.BoolMap(parameters),
}) })
if err != nil { if err != nil {
return fmt.Errorf("Error creating API Gateway Method Response: %s", err) return fmt.Errorf("Error creating API Gateway Method Response: %s", err)
@ -100,6 +113,7 @@ func resourceAwsApiGatewayMethodResponseRead(d *schema.ResourceData, meta interf
log.Printf("[DEBUG] Received API Gateway Method: %s", methodResponse) log.Printf("[DEBUG] Received API Gateway Method: %s", methodResponse)
d.Set("response_models", aws.StringValueMap(methodResponse.ResponseModels)) d.Set("response_models", aws.StringValueMap(methodResponse.ResponseModels))
d.Set("response_parameters_in_json", aws.BoolValueMap(methodResponse.ResponseParameters))
d.SetId(fmt.Sprintf("agmr-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string))) d.SetId(fmt.Sprintf("agmr-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string)))
return nil return nil
@ -115,6 +129,14 @@ func resourceAwsApiGatewayMethodResponseUpdate(d *schema.ResourceData, meta inte
operations = append(operations, expandApiGatewayRequestResponseModelOperations(d, "response_models", "responseModels")...) operations = append(operations, expandApiGatewayRequestResponseModelOperations(d, "response_models", "responseModels")...)
} }
if d.HasChange("response_parameters_in_json") {
ops, err := expandApiGatewayMethodResponseParametersJSONOperations(d, "response_parameters_in_json", "responseParameters")
if err != nil {
return err
}
operations = append(operations, ops...)
}
out, err := conn.UpdateMethodResponse(&apigateway.UpdateMethodResponseInput{ out, err := conn.UpdateMethodResponse(&apigateway.UpdateMethodResponseInput{
HttpMethod: aws.String(d.Get("http_method").(string)), HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)), ResourceId: aws.String(d.Get("resource_id").(string)),

View File

@ -30,6 +30,18 @@ func TestAccAWSAPIGatewayMethodResponse_basic(t *testing.T) {
"aws_api_gateway_method_response.error", "response_models.application/json", "Error"), "aws_api_gateway_method_response.error", "response_models.application/json", "Error"),
), ),
}, },
resource.TestStep{
Config: testAccAWSAPIGatewayMethodResponseConfigUpdate,
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayMethodResponseExists("aws_api_gateway_method_response.error", &conf),
testAccCheckAWSAPIGatewayMethodResponseAttributesUpdate(&conf),
resource.TestCheckResourceAttr(
"aws_api_gateway_method_response.error", "status_code", "400"),
resource.TestCheckResourceAttr(
"aws_api_gateway_method_response.error", "response_models.application/json", "Empty"),
),
},
}, },
}) })
} }
@ -46,6 +58,32 @@ func testAccCheckAWSAPIGatewayMethodResponseAttributes(conf *apigateway.MethodRe
return fmt.Errorf("wrong application/json ResponseModel") return fmt.Errorf("wrong application/json ResponseModel")
} }
} }
if val, ok := conf.ResponseParameters["method.response.header.Content-Type"]; !ok {
return fmt.Errorf("missing Content-Type ResponseParameters")
} else {
if *val != true {
return fmt.Errorf("wrong ResponseParameters value")
}
}
return nil
}
}
func testAccCheckAWSAPIGatewayMethodResponseAttributesUpdate(conf *apigateway.MethodResponse) resource.TestCheckFunc {
return func(s *terraform.State) error {
if *conf.StatusCode == "" {
return fmt.Errorf("empty StatusCode")
}
if val, ok := conf.ResponseModels["application/json"]; !ok {
return fmt.Errorf("missing application/json ResponseModel")
} else {
if *val != "Empty" {
return fmt.Errorf("wrong application/json ResponseModel")
}
}
if conf.ResponseParameters["method.response.header.Content-Type"] != nil {
return fmt.Errorf("Content-Type ResponseParameters shouldn't exist")
}
return nil return nil
} }
} }
@ -145,5 +183,52 @@ resource "aws_api_gateway_method_response" "error" {
response_models = { response_models = {
"application/json" = "Error" "application/json" = "Error"
} }
response_parameters_in_json = <<PARAMS
{
"method.response.header.Content-Type": true
}
PARAMS
}
`
const testAccAWSAPIGatewayMethodResponseConfigUpdate = `
resource "aws_api_gateway_rest_api" "test" {
name = "test"
}
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_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"
}
}
resource "aws_api_gateway_method_response" "error" {
rest_api_id = "${aws_api_gateway_rest_api.test.id}"
resource_id = "${aws_api_gateway_resource.test.id}"
http_method = "${aws_api_gateway_method.test.http_method}"
status_code = "400"
response_models = {
"application/json" = "Empty"
}
response_parameters_in_json = <<PARAMS
{
"method.response.header.Host": true
}
PARAMS
} }
` `

View File

@ -948,6 +948,59 @@ func expandApiGatewayRequestResponseModelOperations(d *schema.ResourceData, key
return operations return operations
} }
func expandApiGatewayMethodResponseParametersJSONOperations(d *schema.ResourceData, key string, prefix string) ([]*apigateway.PatchOperation, error) {
operations := make([]*apigateway.PatchOperation, 0)
oldParameters, newParameters := d.GetChange(key)
oldParametersMap := make(map[string]interface{})
newParametersMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(oldParameters.(string)), &oldParametersMap); err != nil {
err := fmt.Errorf("Error unmarshaling old response_parameters_in_json: %s", err)
return operations, err
}
if err := json.Unmarshal([]byte(newParameters.(string)), &newParametersMap); err != nil {
err := fmt.Errorf("Error unmarshaling new response_parameters_in_json: %s", err)
return operations, err
}
for k, _ := range oldParametersMap {
operation := apigateway.PatchOperation{
Op: aws.String("remove"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, k)),
}
for nK, nV := range newParametersMap {
if nK == k {
operation.Op = aws.String("replace")
operation.Value = aws.String(strconv.FormatBool(nV.(bool)))
}
}
operations = append(operations, &operation)
}
for nK, nV := range newParametersMap {
exists := false
for k, _ := range oldParametersMap {
if k == nK {
exists = true
}
}
if !exists {
operation := apigateway.PatchOperation{
Op: aws.String("add"),
Path: aws.String(fmt.Sprintf("/%s/%s", prefix, nK)),
Value: aws.String(strconv.FormatBool(nV.(bool))),
}
operations = append(operations, &operation)
}
}
return operations, nil
}
func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.PatchOperation { func expandApiGatewayStageKeyOperations(d *schema.ResourceData) []*apigateway.PatchOperation {
operations := make([]*apigateway.PatchOperation, 0) operations := make([]*apigateway.PatchOperation, 0)

View File

@ -66,3 +66,4 @@ The following arguments are supported:
If the backend is an `AWS` Lambda function, the AWS Lambda function error header is matched. If the backend is an `AWS` Lambda function, the AWS Lambda function error header is matched.
For all other `HTTP` and `AWS` backends, the HTTP status code is matched. For all other `HTTP` and `AWS` backends, the HTTP status code is matched.
* `response_templates` - (Optional) A map specifying the templates used to transform the integration response body * `response_templates` - (Optional) A map specifying the templates used to transform the integration response body
* `response_parameters_in_json` - (Optional) A map written as JSON string specifying response parameters that can be read from the backend response

View File

@ -55,3 +55,4 @@ The following arguments are supported:
* `http_method` - (Required) The HTTP Method (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTION`) * `http_method` - (Required) The HTTP Method (`GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTION`)
* `status_code` - (Required) The HTTP status code * `status_code` - (Required) The HTTP status code
* `response_models` - (Optional) A map of the API models used for the response's content type * `response_models` - (Optional) A map of the API models used for the response's content type
* `response_parameters_in_json` - (Optional) A map written as a JSON string representing response parameters that can be sent to the caller