From c168c1e6c5644a5f5f81ab3b94c7b641458b760a Mon Sep 17 00:00:00 2001 From: Anthony Wat Date: Sat, 3 Feb 2024 16:04:56 -0500 Subject: [PATCH] feat: Add introspection and limit args to aws_appsync_graphql_api --- .changelog/35631.txt | 3 + internal/service/appsync/appsync_test.go | 3 + internal/service/appsync/graphql_api.go | 45 +++++++ internal/service/appsync/graphql_api_test.go | 117 ++++++++++++++++++ .../docs/r/appsync_graphql_api.html.markdown | 17 +++ 5 files changed, 185 insertions(+) create mode 100644 .changelog/35631.txt diff --git a/.changelog/35631.txt b/.changelog/35631.txt new file mode 100644 index 00000000000..3002e8b093e --- /dev/null +++ b/.changelog/35631.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_appsync_graphql_api: Add `introspection_config`, `query_depth_limit`, and `resolver_count_limit` arguments +``` \ No newline at end of file diff --git a/internal/service/appsync/appsync_test.go b/internal/service/appsync/appsync_test.go index b81b5ba04a3..de4f7cb5ea2 100644 --- a/internal/service/appsync/appsync_test.go +++ b/internal/service/appsync/appsync_test.go @@ -67,6 +67,9 @@ func TestAccAppSync_serial(t *testing.T) { "AdditionalAuthentication_multiple": testAccGraphQLAPI_AdditionalAuthentication_multiple, "xrayEnabled": testAccGraphQLAPI_xrayEnabled, "visibility": testAccGraphQLAPI_visibility, + "introspectionConfig": testAccGraphQLAPI_introspectionConfig, + "queryDepthLimit": testAccGraphQLAPI_queryDepthLimit, + "resolverCountLimit": testAccGraphQLAPI_resolverCountLimit, }, "Function": { "basic": testAccFunction_basic, diff --git a/internal/service/appsync/graphql_api.go b/internal/service/appsync/graphql_api.go index d7f8ca2cfc1..a2d3f26bb61 100644 --- a/internal/service/appsync/graphql_api.go +++ b/internal/service/appsync/graphql_api.go @@ -136,6 +136,12 @@ func ResourceGraphQLAPI() *schema.Resource { Required: true, ValidateFunc: validation.StringInSlice(appsync.AuthenticationType_Values(), false), }, + "introspection_config": { + Type: schema.TypeString, + Optional: true, + Default: appsync.GraphQLApiIntrospectionConfigEnabled, + ValidateFunc: validation.StringInSlice(appsync.GraphQLApiIntrospectionConfig_Values(), false), + }, "lambda_authorizer_config": { Type: schema.TypeList, Optional: true, @@ -219,6 +225,18 @@ func ResourceGraphQLAPI() *schema.Resource { }, }, }, + "query_depth_limit": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntBetween(0, 75), + }, + "resolver_count_limit": { + Type: schema.TypeInt, + Optional: true, + Default: 0, + ValidateFunc: validation.IntBetween(0, 10000), + }, "schema": { Type: schema.TypeString, Optional: true, @@ -305,6 +323,18 @@ func resourceGraphQLAPICreate(ctx context.Context, d *schema.ResourceData, meta input.UserPoolConfig = expandGraphQLAPIUserPoolConfig(v.([]interface{}), meta.(*conns.AWSClient).Region) } + if v, ok := d.GetOk("introspection_config"); ok { + input.IntrospectionConfig = aws.String(v.(string)) + } + + if v, ok := d.GetOk("query_depth_limit"); ok { + input.QueryDepthLimit = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("resolver_count_limit"); ok { + input.ResolverCountLimit = aws.Int64(int64(v.(int))) + } + if v, ok := d.GetOk("xray_enabled"); ok { input.XrayEnabled = aws.Bool(v.(bool)) } @@ -360,7 +390,10 @@ func resourceGraphQLAPIRead(ctx context.Context, d *schema.ResourceData, meta in if err := d.Set("openid_connect_config", flattenGraphQLAPIOpenIDConnectConfig(api.OpenIDConnectConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting openid_connect_config: %s", err) } + d.Set("introspection_config", api.IntrospectionConfig) d.Set("name", api.Name) + d.Set("query_depth_limit", api.QueryDepthLimit) + d.Set("resolver_count_limit", api.ResolverCountLimit) d.Set("uris", aws.StringValueMap(api.Uris)) if err := d.Set("user_pool_config", flattenGraphQLAPIUserPoolConfig(api.UserPoolConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting user_pool_config: %s", err) @@ -406,6 +439,18 @@ func resourceGraphQLAPIUpdate(ctx context.Context, d *schema.ResourceData, meta input.UserPoolConfig = expandGraphQLAPIUserPoolConfig(v.([]interface{}), meta.(*conns.AWSClient).Region) } + if v, ok := d.GetOk("introspection_config"); ok { + input.IntrospectionConfig = aws.String(v.(string)) + } + + if v, ok := d.GetOk("query_depth_limit"); ok { + input.QueryDepthLimit = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("resolver_count_limit"); ok { + input.ResolverCountLimit = aws.Int64(int64(v.(int))) + } + if v, ok := d.GetOk("xray_enabled"); ok { input.XrayEnabled = aws.Bool(v.(bool)) } diff --git a/internal/service/appsync/graphql_api_test.go b/internal/service/appsync/graphql_api_test.go index 68cc1d6e9d1..6ce82afdd5e 100644 --- a/internal/service/appsync/graphql_api_test.go +++ b/internal/service/appsync/graphql_api_test.go @@ -49,6 +49,9 @@ func testAccGraphQLAPI_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "additional_authentication_provider.#", "0"), resource.TestCheckResourceAttr(resourceName, "xray_enabled", "false"), resource.TestCheckResourceAttr(resourceName, "visibility", "GLOBAL"), + resource.TestCheckResourceAttr(resourceName, "introspection_config", "ENABLED"), + resource.TestCheckResourceAttr(resourceName, "query_depth_limit", "0"), + resource.TestCheckResourceAttr(resourceName, "resolver_count_limit", "0"), ), }, { @@ -1208,6 +1211,90 @@ func testAccGraphQLAPI_visibility(t *testing.T) { }) } +func testAccGraphQLAPI_introspectionConfig(t *testing.T) { + ctx := acctest.Context(t) + var api1 appsync.GraphqlApi + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appsync_graphql_api.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, appsync.EndpointsID) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGraphQLAPIDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGraphQLAPIConfig_introspectionConfig(rName, "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGraphQLAPIExists(ctx, resourceName, &api1), + resource.TestCheckResourceAttr(resourceName, "introspection_config", "DISABLED"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccGraphQLAPI_queryDepthLimit(t *testing.T) { + ctx := acctest.Context(t) + var api1 appsync.GraphqlApi + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appsync_graphql_api.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, appsync.EndpointsID) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGraphQLAPIDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGraphQLAPIConfig_queryDepthLimit(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckGraphQLAPIExists(ctx, resourceName, &api1), + resource.TestCheckResourceAttr(resourceName, "query_depth_limit", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccGraphQLAPI_resolverCountLimit(t *testing.T) { + ctx := acctest.Context(t) + var api1 appsync.GraphqlApi + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appsync_graphql_api.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, appsync.EndpointsID) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGraphQLAPIDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGraphQLAPIConfig_resolverCountLimit(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckGraphQLAPIExists(ctx, resourceName, &api1), + resource.TestCheckResourceAttr(resourceName, "resolver_count_limit", "2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckGraphQLAPIDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).AppSyncConn(ctx) @@ -1707,3 +1794,33 @@ resource "aws_appsync_graphql_api" "test" { } `, rName, xrayEnabled) } + +func testAccGraphQLAPIConfig_introspectionConfig(rName, introspectionConfig string) string { + return fmt.Sprintf(` +resource "aws_appsync_graphql_api" "test" { + authentication_type = "API_KEY" + name = %[1]q + introspection_config = %[2]q +} +`, rName, introspectionConfig) +} + +func testAccGraphQLAPIConfig_queryDepthLimit(rName string, queryDepthLimit int) string { + return fmt.Sprintf(` +resource "aws_appsync_graphql_api" "test" { + authentication_type = "API_KEY" + name = %[1]q + query_depth_limit = %[2]d +} +`, rName, queryDepthLimit) +} + +func testAccGraphQLAPIConfig_resolverCountLimit(rName string, resolverCountLimit int) string { + return fmt.Sprintf(` +resource "aws_appsync_graphql_api" "test" { + authentication_type = "API_KEY" + name = %[1]q + resolver_count_limit = %[2]d +} +`, rName, resolverCountLimit) +} diff --git a/website/docs/r/appsync_graphql_api.html.markdown b/website/docs/r/appsync_graphql_api.html.markdown index fa9a8db7a02..ae04abc1610 100644 --- a/website/docs/r/appsync_graphql_api.html.markdown +++ b/website/docs/r/appsync_graphql_api.html.markdown @@ -198,6 +198,18 @@ resource "aws_wafv2_web_acl" "example" { } ``` +### GraphQL run complexity, query depth, and introspection + +```terraform +resource "aws_appsync_graphql_api" "example" { + authentication_type = "AWS_IAM" + name = "example" + introspection_config = "ENABLED" + query_depth_limit = 2 + resolver_count_limit = 2 +} +``` + ## Argument Reference This resource supports the following arguments: @@ -210,6 +222,11 @@ This resource supports the following arguments: * `lambda_authorizer_config` - (Optional) Nested argument containing Lambda authorizer configuration. Defined below. * `schema` - (Optional) Schema definition, in GraphQL schema language format. Terraform cannot perform drift detection of this configuration. * `additional_authentication_provider` - (Optional) One or more additional authentication providers for the GraphqlApi. Defined below. +* `introspection_config` - (Optional) Sets the value of the GraphQL API to enable (`ENABLED`) or disable (`DISABLED`) introspection. If no value is provided, the introspection configuration will be set to ENABLED by default. This field will produce an error if the operation attempts to use the introspection feature while this field is disabled. For more information about introspection, see [GraphQL introspection](https://graphql.org/learn/introspection/). +* `query_depth_limit` - (Optional) The maximum depth a query can have in a single request. Depth refers to the amount of nested levels allowed in the body of query. The default value is `0` (or unspecified), which indicates there's no depth limit. If you set a limit, it can be between `1` and `75` nested levels. This field will produce a limit error if the operation falls out of bounds. + + Note that fields can still be set to nullable or non-nullable. If a non-nullable field produces an error, the error will be thrown upwards to the first nullable field available. +* `resolver_count_limit` - (Optional) The maximum number of resolvers that can be invoked in a single request. The default value is `0` (or unspecified), which will set the limit to `10000`. When specified, the limit value can be between `1` and `10000`. This field will produce a limit error if the operation falls out of bounds. * `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. * `xray_enabled` - (Optional) Whether tracing with X-ray is enabled. Defaults to false. * `visibility` - (Optional) Sets the value of the GraphQL API to public (`GLOBAL`) or private (`PRIVATE`). If no value is provided, the visibility will be set to `GLOBAL` by default. This value cannot be changed once the API has been created.