diff --git a/.changelog/28057.txt b/.changelog/28057.txt new file mode 100644 index 000000000000..4207aabddec6 --- /dev/null +++ b/.changelog/28057.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_appsync_function: Add `runtime` and `code` arguments +``` + +```release-note:enhancement +resource/aws_appsync_function: Make `request_mapping_template` and `response_mapping_template` Optional +``` diff --git a/internal/service/appsync/appsync_test.go b/internal/service/appsync/appsync_test.go index 26faa7f6e65c..4bc16aabb417 100644 --- a/internal/service/appsync/appsync_test.go +++ b/internal/service/appsync/appsync_test.go @@ -61,6 +61,7 @@ func TestAccAppSync_serial(t *testing.T) { }, "Function": { "basic": testAccFunction_basic, + "code": testAccFunction_code, "disappears": testAccFunction_disappears, "description": testAccFunction_description, "responseMappingTemplate": testAccFunction_responseMappingTemplate, diff --git a/internal/service/appsync/function.go b/internal/service/appsync/function.go index c30fc76bbf28..baf6abc8011f 100644 --- a/internal/service/appsync/function.go +++ b/internal/service/appsync/function.go @@ -32,10 +32,36 @@ func ResourceFunction() *schema.Resource { Required: true, ForceNew: true, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "code": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"runtime"}, + ValidateFunc: validation.StringLenBetween(1, 32768), + }, "data_source": { Type: schema.TypeString, Required: true, }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "function_id": { + Type: schema.TypeString, + Computed: true, + }, + "function_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice([]string{ + "2018-05-29", + }, true), + }, "max_batch_size": { Type: schema.TypeInt, Optional: true, @@ -47,28 +73,31 @@ func ResourceFunction() *schema.Resource { ValidateFunc: validation.StringMatch(regexp.MustCompile(`[_A-Za-z][_0-9A-Za-z]*`), "must match [_A-Za-z][_0-9A-Za-z]*"), }, "request_mapping_template": { - Type: schema.TypeString, - Required: true, - }, - "response_mapping_template": { - Type: schema.TypeString, - Required: true, - }, - "description": { Type: schema.TypeString, Optional: true, }, - "function_version": { + "response_mapping_template": { Type: schema.TypeString, Optional: true, - Default: "2018-05-29", - ValidateFunc: validation.StringInSlice([]string{ - "2018-05-29", - }, true), }, - "arn": { - Type: schema.TypeString, - Computed: true, + "runtime": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + RequiredWith: []string{"code"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(appsync.RuntimeName_Values(), false), + }, + "runtime_version": { + Type: schema.TypeString, + Required: true, + }, + }, + }, }, "sync_config": { Type: schema.TypeList, @@ -103,10 +132,6 @@ func ResourceFunction() *schema.Resource { }, }, }, - "function_id": { - Type: schema.TypeString, - Computed: true, - }, }, } } @@ -117,17 +142,25 @@ func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { apiID := d.Get("api_id").(string) input := &appsync.CreateFunctionInput{ - ApiId: aws.String(apiID), - DataSourceName: aws.String(d.Get("data_source").(string)), - FunctionVersion: aws.String(d.Get("function_version").(string)), - Name: aws.String(d.Get("name").(string)), - RequestMappingTemplate: aws.String(d.Get("request_mapping_template").(string)), + ApiId: aws.String(apiID), + DataSourceName: aws.String(d.Get("data_source").(string)), + FunctionVersion: aws.String(d.Get("function_version").(string)), + Name: aws.String(d.Get("name").(string)), + } + + if v, ok := d.GetOk("code"); ok { + input.Code = aws.String(v.(string)) } if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } + if v, ok := d.GetOk("request_mapping_template"); ok { + input.RequestMappingTemplate = aws.String(v.(string)) + input.FunctionVersion = aws.String("2018-05-29") + } + if v, ok := d.GetOk("response_mapping_template"); ok { input.ResponseMappingTemplate = aws.String(v.(string)) } @@ -140,9 +173,13 @@ func resourceFunctionCreate(d *schema.ResourceData, meta interface{}) error { input.SyncConfig = expandSyncConfig(v.([]interface{})) } + if v, ok := d.GetOk("runtime"); ok && len(v.([]interface{})) > 0 { + input.Runtime = expandRuntime(v.([]interface{})) + } + resp, err := conn.CreateFunction(input) if err != nil { - return fmt.Errorf("Error creating AppSync Function: %w", err) + return fmt.Errorf("error creating AppSync Function: %w", err) } d.SetId(fmt.Sprintf("%s-%s", apiID, aws.StringValue(resp.FunctionConfiguration.FunctionId))) @@ -170,7 +207,7 @@ func resourceFunctionRead(d *schema.ResourceData, meta interface{}) error { return nil } if err != nil { - return fmt.Errorf("Error getting AppSync Function %s: %w", d.Id(), err) + return fmt.Errorf("error getting AppSync Function %s: %w", d.Id(), err) } function := resp.FunctionConfiguration @@ -184,11 +221,16 @@ func resourceFunctionRead(d *schema.ResourceData, meta interface{}) error { d.Set("request_mapping_template", function.RequestMappingTemplate) d.Set("response_mapping_template", function.ResponseMappingTemplate) d.Set("max_batch_size", function.MaxBatchSize) + d.Set("code", function.Code) if err := d.Set("sync_config", flattenSyncConfig(function.SyncConfig)); err != nil { return fmt.Errorf("error setting sync_config: %w", err) } + if err := d.Set("runtime", flattenRuntime(function.Runtime)); err != nil { + return fmt.Errorf("error setting runtime: %w", err) + } + return nil } @@ -201,18 +243,25 @@ func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { } input := &appsync.UpdateFunctionInput{ - ApiId: aws.String(apiID), - DataSourceName: aws.String(d.Get("data_source").(string)), - FunctionId: aws.String(functionID), - FunctionVersion: aws.String(d.Get("function_version").(string)), - Name: aws.String(d.Get("name").(string)), - RequestMappingTemplate: aws.String(d.Get("request_mapping_template").(string)), + ApiId: aws.String(apiID), + DataSourceName: aws.String(d.Get("data_source").(string)), + FunctionId: aws.String(functionID), + FunctionVersion: aws.String(d.Get("function_version").(string)), + Name: aws.String(d.Get("name").(string)), } if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } + if v, ok := d.GetOk("code"); ok { + input.Code = aws.String(v.(string)) + } + + if v, ok := d.GetOk("request_mapping_template"); ok { + input.RequestMappingTemplate = aws.String(v.(string)) + } + if v, ok := d.GetOk("response_mapping_template"); ok { input.ResponseMappingTemplate = aws.String(v.(string)) } @@ -225,9 +274,13 @@ func resourceFunctionUpdate(d *schema.ResourceData, meta interface{}) error { input.SyncConfig = expandSyncConfig(v.([]interface{})) } + if v, ok := d.GetOk("runtime"); ok && len(v.([]interface{})) > 0 { + input.Runtime = expandRuntime(v.([]interface{})) + } + _, err = conn.UpdateFunction(input) if err != nil { - return fmt.Errorf("Error updating AppSync Function %s: %w", d.Id(), err) + return fmt.Errorf("error updating AppSync Function %s: %w", d.Id(), err) } return resourceFunctionRead(d, meta) @@ -251,7 +304,7 @@ func resourceFunctionDelete(d *schema.ResourceData, meta interface{}) error { return nil } if err != nil { - return fmt.Errorf("Error deleting AppSync Function %s: %w", d.Id(), err) + return fmt.Errorf("error deleting AppSync Function %s: %w", d.Id(), err) } return nil @@ -265,6 +318,39 @@ func DecodeFunctionID(id string) (string, string, error) { return idParts[0], idParts[1], nil } +func expandRuntime(l []interface{}) *appsync.AppSyncRuntime { + if len(l) == 0 || l[0] == nil { + return nil + } + + configured := l[0].(map[string]interface{}) + + result := &appsync.AppSyncRuntime{} + + if v, ok := configured["name"].(string); ok { + result.Name = aws.String(v) + } + + if v, ok := configured["runtime_version"].(string); ok { + result.RuntimeVersion = aws.String(v) + } + + return result +} + +func flattenRuntime(config *appsync.AppSyncRuntime) []map[string]interface{} { + if config == nil { + return nil + } + + result := map[string]interface{}{ + "name": aws.StringValue(config.Name), + "runtime_version": aws.StringValue(config.RuntimeVersion), + } + + return []map[string]interface{}{result} +} + func expandSyncConfig(l []interface{}) *appsync.SyncConfig { if len(l) == 0 || l[0] == nil { return nil diff --git a/internal/service/appsync/function_test.go b/internal/service/appsync/function_test.go index 61308578582b..74d7ccf23002 100644 --- a/internal/service/appsync/function_test.go +++ b/internal/service/appsync/function_test.go @@ -37,6 +37,7 @@ func testAccFunction_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "name", rName2), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "max_batch_size", "0"), + resource.TestCheckResourceAttr(resourceName, "runtime.#", "0"), resource.TestCheckResourceAttr(resourceName, "sync_config.#", "0"), resource.TestCheckResourceAttrPair(resourceName, "api_id", "aws_appsync_graphql_api.test", "id"), resource.TestCheckResourceAttrPair(resourceName, "data_source", "aws_appsync_datasource.test", "name"), @@ -58,6 +59,49 @@ func testAccFunction_basic(t *testing.T) { }) } +func testAccFunction_code(t *testing.T) { + rName1 := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) + rName2 := fmt.Sprintf("tfexample%s", sdkacctest.RandString(8)) + resourceName := "aws_appsync_function.test" + var config appsync.FunctionConfiguration + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(appsync.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, appsync.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFunctionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFunctionConfig_code(rName1, rName2, "test-fixtures/test-code.js"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttrSet(resourceName, "code"), + resource.TestCheckResourceAttr(resourceName, "runtime.#", "1"), + resource.TestCheckResourceAttr(resourceName, "runtime.0.name", "APPSYNC_JS"), + resource.TestCheckResourceAttr(resourceName, "runtime.0.runtime_version", "1.0.0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccFunctionConfig_code(rName1, rName2, "test-fixtures/test-code-updated.js"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFunctionExists(resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "name", rName2), + resource.TestCheckResourceAttrSet(resourceName, "code"), + resource.TestCheckResourceAttr(resourceName, "runtime.#", "1"), + resource.TestCheckResourceAttr(resourceName, "runtime.0.name", "APPSYNC_JS"), + resource.TestCheckResourceAttr(resourceName, "runtime.0.runtime_version", "1.0.0"), + ), + }, + }, + }) +} + func testAccFunction_syncConfig(t *testing.T) { rName := fmt.Sprintf("tfacctest%d", sdkacctest.RandInt()) resourceName := "aws_appsync_function.test" @@ -233,13 +277,11 @@ func testAccCheckFunctionExists(name string, config *appsync.FunctionConfigurati } func testAccFunctionConfig_basic(r1, r2, region string) string { - return fmt.Sprintf(` -%[1]s - + return acctest.ConfigCompose(testAccDataSourceConfig_dynamoDBRegion(r1, region), fmt.Sprintf(` resource "aws_appsync_function" "test" { api_id = aws_appsync_graphql_api.test.id data_source = aws_appsync_datasource.test.name - name = "%[2]s" + name = %[1]q request_mapping_template = <