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
import (
"encoding/json"
"fmt"
"log"
"time"
@ -54,6 +55,11 @@ func resourceAwsApiGatewayIntegrationResponse() *schema.Resource {
Optional: true,
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)
}
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{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
RestApiId: aws.String(d.Get("rest_api_id").(string)),
StatusCode: aws.String(d.Get("status_code").(string)),
ResponseTemplates: aws.StringMap(templates),
// TODO implement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: nil,
// TODO reimplement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: aws.StringMap(parameters),
}
if v, ok := d.GetOk("selection_pattern"); ok {
input.SelectionPattern = aws.String(v.(string))
@ -106,12 +119,13 @@ func resourceAwsApiGatewayIntegrationResponseRead(d *schema.ResourceData, meta i
}
return err
}
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.Set("response_templates", integrationResponse.ResponseTemplates)
d.Set("selection_pattern", integrationResponse.SelectionPattern)
d.Set("response_parameters_in_json", aws.StringValueMap(integrationResponse.ResponseParameters))
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{ }"),
),
},
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 != ".*" {
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
}
}
@ -147,6 +184,12 @@ resource "aws_api_gateway_method_response" "error" {
response_models = {
"application/json" = "Error"
}
response_parameters_in_json = <<PARAMS
{
"method.response.header.Content-Type": true
}
PARAMS
}
resource "aws_api_gateway_integration" "test" {
@ -173,5 +216,77 @@ resource "aws_api_gateway_integration_response" "test" {
"application/json" = ""
"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
import (
"encoding/json"
"fmt"
"log"
"time"
@ -49,6 +50,11 @@ func resourceAwsApiGatewayMethodResponse() *schema.Resource {
Optional: true,
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)
}
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{
HttpMethod: aws.String(d.Get("http_method").(string)),
ResourceId: aws.String(d.Get("resource_id").(string)),
RestApiId: aws.String(d.Get("rest_api_id").(string)),
StatusCode: aws.String(d.Get("status_code").(string)),
ResponseModels: aws.StringMap(models),
// TODO implement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: nil,
// TODO reimplement once [GH-2143](https://github.com/hashicorp/terraform/issues/2143) has been implemented
ResponseParameters: aws.BoolMap(parameters),
})
if err != nil {
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)
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)))
return nil
@ -115,6 +129,14 @@ func resourceAwsApiGatewayMethodResponseUpdate(d *schema.ResourceData, meta inte
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{
HttpMethod: aws.String(d.Get("http_method").(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"),
),
},
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")
}
}
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
}
}
@ -145,5 +183,52 @@ resource "aws_api_gateway_method_response" "error" {
response_models = {
"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
}
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 {
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.
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_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`)
* `status_code` - (Required) The HTTP status code
* `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