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

Added API Gateway Gateway response resource #1168

Merged
merged 1 commit into from
Jul 18, 2017
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
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ func Provider() terraform.ResourceProvider {
"aws_api_gateway_client_certificate": resourceAwsApiGatewayClientCertificate(),
"aws_api_gateway_deployment": resourceAwsApiGatewayDeployment(),
"aws_api_gateway_domain_name": resourceAwsApiGatewayDomainName(),
"aws_api_gateway_gateway_response": resourceAwsApiGatewayGatewayResponse(),
"aws_api_gateway_integration": resourceAwsApiGatewayIntegration(),
"aws_api_gateway_integration_response": resourceAwsApiGatewayIntegrationResponse(),
"aws_api_gateway_method": resourceAwsApiGatewayMethod(),
Expand Down
145 changes: 145 additions & 0 deletions aws/resource_aws_api_gateway_gateway_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsApiGatewayGatewayResponse() *schema.Resource {
return &schema.Resource{
Create: resourceAwsApiGatewayGatewayResponsePut,
Read: resourceAwsApiGatewayGatewayResponseRead,
Update: resourceAwsApiGatewayGatewayResponsePut,
Delete: resourceAwsApiGatewayGatewayResponseDelete,

Schema: map[string]*schema.Schema{
"rest_api_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"response_type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"status_code": {
Type: schema.TypeString,
Optional: true,
},

"response_templates": {
Type: schema.TypeMap,
Elem: schema.TypeString,
Optional: true,
},

"response_parameters": {
Type: schema.TypeMap,
Elem: schema.TypeString,
Optional: true,
},
},
}
}

func resourceAwsApiGatewayGatewayResponsePut(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway

templates := make(map[string]string)
if kv, ok := d.GetOk("response_templates"); ok {
for k, v := range kv.(map[string]interface{}) {
templates[k] = v.(string)
}
}

parameters := make(map[string]string)
if kv, ok := d.GetOk("response_parameters"); ok {
for k, v := range kv.(map[string]interface{}) {
parameters[k] = v.(string)
}
}

input := apigateway.PutGatewayResponseInput{
RestApiId: aws.String(d.Get("rest_api_id").(string)),
ResponseType: aws.String(d.Get("response_type").(string)),
ResponseTemplates: aws.StringMap(templates),
ResponseParameters: aws.StringMap(parameters),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the two fields (response_templates & response_parameters) are optional and user didn't define those we shouldn't be sending them to the API here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed via Slack, this doesn't matter and we need it for updates, so 👍

}

if v, ok := d.GetOk("status_code"); ok {
input.StatusCode = aws.String(v.(string))
}

log.Printf("[DEBUG] Putting API Gateway Gateway Response: %s", input)

_, err := conn.PutGatewayResponse(&input)
if err != nil {
return fmt.Errorf("Error putting API Gateway Gateway Response: %s", err)
}

d.SetId(fmt.Sprintf("aggr-%s-%s", d.Get("rest_api_id").(string), d.Get("response_type").(string)))
log.Printf("[DEBUG] API Gateway Gateway Response put (%q)", d.Id())

return resourceAwsApiGatewayGatewayResponseRead(d, meta)
}

func resourceAwsApiGatewayGatewayResponseRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway

log.Printf("[DEBUG] Reading API Gateway Gateway Response %s", d.Id())
gatewayResponse, err := conn.GetGatewayResponse(&apigateway.GetGatewayResponseInput{
RestApiId: aws.String(d.Get("rest_api_id").(string)),
ResponseType: aws.String(d.Get("response_type").(string)),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the combination of both rest_api_id and response_type is treated as "identifier" for the purpose of the lookup here shouldn't response_type also be ForceNew?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, don't see any counter argument :)

})
if err != nil {
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" {
log.Printf("[WARN] API Gateway Gateway Response (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌 👍

}
return err
}

log.Printf("[DEBUG] Received API Gateway Gateway Response: %s", gatewayResponse)

d.Set("response_type", gatewayResponse.ResponseType)
d.Set("status_code", gatewayResponse.StatusCode)
d.Set("response_templates", aws.StringValueMap(gatewayResponse.ResponseTemplates))
d.Set("response_parameters", aws.StringValueMap(gatewayResponse.ResponseParameters))

return nil
}

func resourceAwsApiGatewayGatewayResponseDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).apigateway
log.Printf("[DEBUG] Deleting API Gateway Gateway Response: %s", d.Id())

return resource.Retry(1*time.Minute, func() *resource.RetryError {
_, err := conn.DeleteGatewayResponse(&apigateway.DeleteGatewayResponseInput{
RestApiId: aws.String(d.Get("rest_api_id").(string)),
ResponseType: aws.String(d.Get("response_type").(string)),
})

if err == nil {
return nil
}

apigatewayErr, ok := err.(awserr.Error)

if ok && apigatewayErr.Code() == "NotFoundException" {
return nil
}

return resource.NonRetryableError(err)
})
}
148 changes: 148 additions & 0 deletions aws/resource_aws_api_gateway_gateway_response_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package aws

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSAPIGatewayGatewayResponse_basic(t *testing.T) {
var conf apigateway.UpdateGatewayResponseOutput

rName := acctest.RandString(10)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAPIGatewayGatewayResponseDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSAPIGatewayGatewayResponseConfig(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayGatewayResponseExists("aws_api_gateway_gateway_response.test", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_gateway_response.test", "status_code", "401"),
resource.TestCheckResourceAttr("aws_api_gateway_gateway_response.test", "response_parameters.gatewayresponse.header.Authorization", "'Basic'"),
resource.TestCheckResourceAttr("aws_api_gateway_gateway_response.test", "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"),
resource.TestCheckNoResourceAttr("aws_api_gateway_gateway_response.test", "response_templates.application/json"),
),
},

{
Config: testAccAWSAPIGatewayGatewayResponseConfigUpdate(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSAPIGatewayGatewayResponseExists("aws_api_gateway_gateway_response.test", &conf),
resource.TestCheckResourceAttr("aws_api_gateway_gateway_response.test", "status_code", "477"),
resource.TestCheckResourceAttr("aws_api_gateway_gateway_response.test", "response_templates.application/json", "{'message':$context.error.messageString}"),
resource.TestCheckNoResourceAttr("aws_api_gateway_gateway_response.test", "response_templates.application/xml"),
resource.TestCheckNoResourceAttr("aws_api_gateway_gateway_response.test", "response_parameters.gatewayresponse.header.Authorization"),
),
},
},
})
}

func testAccCheckAWSAPIGatewayGatewayResponseExists(n string, res *apigateway.UpdateGatewayResponseOutput) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No API Gateway Gateway Response ID is set")
}

conn := testAccProvider.Meta().(*AWSClient).apigateway

req := &apigateway.GetGatewayResponseInput{
RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.main"].Primary.ID),
ResponseType: aws.String(rs.Primary.Attributes["response_type"]),
}
describe, err := conn.GetGatewayResponse(req)
if err != nil {
return err
}

*res = *describe

return nil
}
}

func testAccCheckAWSAPIGatewayGatewayResponseDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).apigateway

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_api_gateway_gateway_response" {
continue
}

req := &apigateway.GetGatewayResponseInput{
RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.main"].Primary.ID),
ResponseType: aws.String(rs.Primary.Attributes["response_type"]),
}
_, err := conn.GetGatewayResponse(req)

if err == nil {
return fmt.Errorf("API Gateway Gateway Response still exists")
}

aws2err, ok := err.(awserr.Error)
if !ok {
return err
}
if aws2err.Code() != "NotFoundException" {
return err
}

return nil
}

return nil
}

func testAccAWSAPIGatewayGatewayResponseConfig(rName string) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "main" {
name = "%s"
}

resource "aws_api_gateway_gateway_response" "test" {
rest_api_id = "${aws_api_gateway_rest_api.main.id}"
status_code = "401"
response_type = "UNAUTHORIZED"

response_templates = {
"application/xml" = "#set($inputRoot = $input.path('$'))\n{ }"
}

response_parameters = {
"gatewayresponse.header.Authorization" = "'Basic'"
}
}
`, rName)
}

func testAccAWSAPIGatewayGatewayResponseConfigUpdate(rName string) string {
return fmt.Sprintf(`
resource "aws_api_gateway_rest_api" "main" {
name = "%s"
}

resource "aws_api_gateway_gateway_response" "test" {
rest_api_id = "${aws_api_gateway_rest_api.main.id}"
status_code = "477"
response_type = "UNAUTHORIZED"

response_templates = {
"application/json" = "{'message':$context.error.messageString}"
}
}
`, rName)
}
3 changes: 3 additions & 0 deletions website/aws.erb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@
<li<%= sidebar_current("docs-aws-resource-api-gateway-domain-name") %>>
<a href="/docs/providers/aws/r/api_gateway_domain_name.html">aws_api_gateway_domain_name</a>
</li>
<li<%= sidebar_current("docs-aws-resource-api-gateway-gateway-response") %>>
<a href="/docs/providers/aws/r/api_gateway_gateway_response.html">aws_api_gateway_gateway_response</a>
</li>
<li<%= sidebar_current("docs-aws-resource-api-gateway-integration") %>>
<a href="/docs/providers/aws/r/api_gateway_integration.html">aws_api_gateway_integration</a>
</li>
Expand Down
43 changes: 43 additions & 0 deletions website/docs/r/api_gateway_gateway_response.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
layout: "aws"
page_title: "AWS: aws_api_gateway_gateway_response"
sidebar_current: "docs-aws-resource-api-gateway-gateway-response"
description: |-
Provides an API Gateway Gateway Response for a REST API Gateway.
---

# aws\_api\_gateway\_gateway\_response

Provides an API Gateway Gateway Response for a REST API Gateway.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... Gateway Gateway ... Gateway 😂 🤷‍♂️


## Example Usage

```hcl
resource "aws_api_gateway_rest_api" "main" {
name = "MyDemoAPI"
}

resource "aws_api_gateway_gateway_response" "test" {
rest_api_id = "${aws_api_gateway_rest_api.main.id}"
status_code = "401"
response_type = "UNAUTHORIZED"

response_templates = {
"application/json" = "{'message':$context.error.messageString}"
}

response_parameters = {
"gatewayresponse.header.Authorization" = "'Basic'"
}
}
```

## Argument Reference

The following arguments are supported:

* `rest_api_id` - (Required) The string identifier of the associated REST API.
* `response_type` - (Required) The response type of the associated GatewayResponse.
* `status_code` - (Optional) The HTTP status code of the Gateway Response.
* `response_parameters` - (Optional) A map specifying the templates used to transform the response body.
* `response_templates` - (Optional) A map specifying the parameters (paths, query strings and headers) of the Gateway Response.