-
Notifications
You must be signed in to change notification settings - Fork 9.1k
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 support for appsync_graphql_api schema dn appsync_resolver #6451
Changes from 2 commits
6c8d7a7
d35ae0d
6492cd3
85574e5
40331c9
2a80fdb
c708371
2dec7a2
31d3034
2e919d5
15e2e23
8363ab0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,160 @@ | ||||||||||||||||||||
package aws | ||||||||||||||||||||
|
||||||||||||||||||||
import ( | ||||||||||||||||||||
"fmt" | ||||||||||||||||||||
"github.com/aws/aws-sdk-go/aws" | ||||||||||||||||||||
"github.com/aws/aws-sdk-go/service/appsync" | ||||||||||||||||||||
"github.com/hashicorp/terraform/helper/schema" | ||||||||||||||||||||
"strings" | ||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
func resourceAwsAppsyncResolver() *schema.Resource { | ||||||||||||||||||||
return &schema.Resource{ | ||||||||||||||||||||
Create: resourceAwsAppsyncResolverCreate, | ||||||||||||||||||||
Read: resourceAwsAppsyncResolverRead, | ||||||||||||||||||||
Update: resourceAwsAppsyncResolverUpdate, | ||||||||||||||||||||
Delete: resourceAwsAppsyncResolverDelete, | ||||||||||||||||||||
|
||||||||||||||||||||
Importer: &schema.ResourceImporter{ | ||||||||||||||||||||
State: schema.ImportStatePassthrough, | ||||||||||||||||||||
}, | ||||||||||||||||||||
|
||||||||||||||||||||
Schema: map[string]*schema.Schema{ | ||||||||||||||||||||
"api_id": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, | ||||||||||||||||||||
ForceNew: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
"type": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, | ||||||||||||||||||||
ForceNew: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
"field": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, | ||||||||||||||||||||
ForceNew: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
"data_source": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
"request_template": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
"response_template": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Required: true, // documentation bug, the api returns 400 if this is not specified. | ||||||||||||||||||||
}, | ||||||||||||||||||||
"arn": { | ||||||||||||||||||||
Type: schema.TypeString, | ||||||||||||||||||||
Computed: true, | ||||||||||||||||||||
}, | ||||||||||||||||||||
}, | ||||||||||||||||||||
} | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func resourceAwsAppsyncResolverCreate(d *schema.ResourceData, meta interface{}) error { | ||||||||||||||||||||
conn := meta.(*AWSClient).appsyncconn | ||||||||||||||||||||
|
||||||||||||||||||||
input := &appsync.CreateResolverInput{ | ||||||||||||||||||||
ApiId: aws.String(d.Get("api_id").(string)), | ||||||||||||||||||||
DataSourceName: aws.String(d.Get("data_source").(string)), | ||||||||||||||||||||
TypeName: aws.String(d.Get("type").(string)), | ||||||||||||||||||||
FieldName: aws.String(d.Get("field").(string)), | ||||||||||||||||||||
RequestMappingTemplate: aws.String(d.Get("request_template").(string)), | ||||||||||||||||||||
ResponseMappingTemplate: aws.String(d.Get("response_template").(string)), | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
_, err := conn.CreateResolver(input) | ||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return error context here for operators and code maintainers:
Suggested change
|
||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
d.SetId(d.Get("api_id").(string) + "-" + d.Get("type").(string) + "-" + d.Get("field").(string)) | ||||||||||||||||||||
|
||||||||||||||||||||
return resourceAwsAppsyncResolverRead(d, meta) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func resourceAwsAppsyncResolverRead(d *schema.ResourceData, meta interface{}) error { | ||||||||||||||||||||
conn := meta.(*AWSClient).appsyncconn | ||||||||||||||||||||
|
||||||||||||||||||||
apiID, typeName, fieldName, err := decodeAppsyncResolverID(d.Id()) | ||||||||||||||||||||
|
||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
input := &appsync.GetResolverInput{ | ||||||||||||||||||||
ApiId: aws.String(apiID), | ||||||||||||||||||||
TypeName: aws.String(typeName), | ||||||||||||||||||||
FieldName: aws.String(fieldName), | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
resp, err := conn.GetResolver(input) | ||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prior to returning an error when an AppSync Resolver cannot be found (which can happen when the GraphQL API or Resolver is deleted outside Terraform), we should instead attempt to catch the error and signal to Terraform that this resource needs to be recreated instead.
Suggested change
By returning the func TestAccAwsAppsyncResolver_disappears(t *testing.T) {
var api1 appsync.GraphqlApi
var resolver1 appsync.Resolver
rName := fmt.Sprintf("tfacctest%d", acctest.RandInt())
appsyncGraphqlApiResourceName := "aws_appsync_graphql_api.test"
resourceName := "aws_appsync_resolver.test"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAwsAppsyncResolverDestroy,
Steps: []resource.TestStep{
{
Config: testAccAppsyncResolver_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsAppsyncGraphqlApiExists(appsyncGraphqlApiResourceName, &api1),
testAccCheckAwsAppsyncResolverExists(resourceName, &resolver1),
testAccCheckAwsAppsyncResolverDisappears(&api1, &resolver1),
),
ExpectNonEmptyPlan: true,
},
},
})
}
func testAccCheckAwsAppsyncResolverDisappears(api *appsync.GraphqlApi, resolver *appsync.Resolver) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appsyncconn
input := &appsync.DeleteResolverInput{
ApiId: api.ApiId,
FieldName: resolver.FieldName,
TypeName: resolver.TypeName,
}
_, err := conn.DeleteResolver(input)
return err
}
} Prior to resource code update:
After code update:
|
||||||||||||||||||||
return err | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return error context here for operators and code maintainers:
Suggested change
|
||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
d.Set("api_id", apiID) | ||||||||||||||||||||
d.Set("arn", resp.Resolver.ResolverArn) | ||||||||||||||||||||
d.Set("type", resp.Resolver.TypeName) | ||||||||||||||||||||
d.Set("field", resp.Resolver.FieldName) | ||||||||||||||||||||
d.Set("data_source", resp.Resolver.DataSourceName) | ||||||||||||||||||||
d.Set("request_template", resp.Resolver.RequestMappingTemplate) | ||||||||||||||||||||
d.Set("response_template", resp.Resolver.ResponseMappingTemplate) | ||||||||||||||||||||
|
||||||||||||||||||||
return nil | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func resourceAwsAppsyncResolverUpdate(d *schema.ResourceData, meta interface{}) error { | ||||||||||||||||||||
conn := meta.(*AWSClient).appsyncconn | ||||||||||||||||||||
|
||||||||||||||||||||
input := &appsync.UpdateResolverInput{ | ||||||||||||||||||||
ApiId: aws.String(d.Get("api_id").(string)), | ||||||||||||||||||||
DataSourceName: aws.String(d.Get("data_source").(string)), | ||||||||||||||||||||
FieldName: aws.String(d.Get("field").(string)), | ||||||||||||||||||||
TypeName: aws.String(d.Get("type").(string)), | ||||||||||||||||||||
RequestMappingTemplate: aws.String(d.Get("request_template").(string)), | ||||||||||||||||||||
ResponseMappingTemplate: aws.String(d.Get("response_template").(string)), | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
_, err := conn.UpdateResolver(input) | ||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return error context here for operators and code maintainers:
Suggested change
|
||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return resourceAwsAppsyncResolverRead(d, meta) | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func resourceAwsAppsyncResolverDelete(d *schema.ResourceData, meta interface{}) error { | ||||||||||||||||||||
conn := meta.(*AWSClient).appsyncconn | ||||||||||||||||||||
|
||||||||||||||||||||
apiID, typeName, fieldName, err := decodeAppsyncResolverID(d.Id()) | ||||||||||||||||||||
|
||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
input := &appsync.DeleteResolverInput{ | ||||||||||||||||||||
ApiId: aws.String(apiID), | ||||||||||||||||||||
TypeName: aws.String(typeName), | ||||||||||||||||||||
FieldName: aws.String(fieldName), | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
_, err = conn.DeleteResolver(input) | ||||||||||||||||||||
if err != nil { | ||||||||||||||||||||
return err | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return error context here for operators and code maintainers:
Suggested change
|
||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
return nil | ||||||||||||||||||||
} | ||||||||||||||||||||
|
||||||||||||||||||||
func decodeAppsyncResolverID(id string) (string, string, string, error) { | ||||||||||||||||||||
idParts := strings.SplitN(id, "-", 3) | ||||||||||||||||||||
if len(idParts) != 3 { | ||||||||||||||||||||
return "", "", "", fmt.Errorf("expected ID in format ApiID-TypeName-FieldName, received: %s", id) | ||||||||||||||||||||
} | ||||||||||||||||||||
return idParts[0], idParts[1], idParts[2], nil | ||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to this pull request -- when running the acceptance testing for
aws_appsync_graphql_api
, was hitting this pretty often with random tests and our default 20 concurrency:Judging by the lack of error context before
ConcurrentModificationException
here and from the debug logs this was occurring during theCreateGraphqlApi
call. In a followup commit to ensure a great user experience, I went ahead and added the error context to that call so we can know where this similar looking error is happening (return fmt.Errorf("error creating AppSync GraphQL API: %d", err)
) and updated the AWS Go SDK AppSync client to automatically enable retries for that call inaws/config.go
:I mention this here because we may be able to remove the wrapping
retryOnAwsCode
calls below in theaws_graphql_resolver
resource by adding other API calls with their appropriate error messaging to the above logic. If you are interested in simplifying the code after this pull request, we'd happily accept that. 👍