diff --git a/.changelog/1726.txt b/.changelog/1726.txt new file mode 100644 index 0000000000..82ea32c841 --- /dev/null +++ b/.changelog/1726.txt @@ -0,0 +1,19 @@ +```release-note:new-data-source +tencentcloud_api_gateway_api_docs +``` + +```release-note:new-resource +tencentcloud_api_gateway_api_doc +``` + +```release-note:new-data-source +tencentcloud_api_gateway_api_apps +``` + +```release-note:new-resource +tencentcloud_api_gateway_api_app +``` + +```release-note:enhancement +resource/tencentcloud_api_gateway_custom_domain: support add `is_forced_https` params +``` \ No newline at end of file diff --git a/examples/tencentcloud-api-gateway/main.tf b/examples/tencentcloud-api-gateway/main.tf index 6ffde31e3c..17d1dbabc3 100644 --- a/examples/tencentcloud-api-gateway/main.tf +++ b/examples/tencentcloud-api-gateway/main.tf @@ -102,6 +102,13 @@ resource "tencentcloud_api_gateway_strategy_attachment" "test"{ bind_api_id = tencentcloud_api_gateway_api.api.id } +resource "tencentcloud_api_gateway_api_doc" "my_api_doc" { + api_doc_name = "create_doc_test" + service_id = tencentcloud_api_gateway_service_release.service.service_id + environment = "release" + api_ids = [tencentcloud_api_gateway_api.api.id] +} + data "tencentcloud_api_gateway_api_keys" "name" { secret_name = tencentcloud_api_gateway_api_key.test.secret_name } diff --git a/tencentcloud/data_source_tc_api_gateway_api_apps.go b/tencentcloud/data_source_tc_api_gateway_api_apps.go new file mode 100644 index 0000000000..a900338da4 --- /dev/null +++ b/tencentcloud/data_source_tc_api_gateway_api_apps.go @@ -0,0 +1,170 @@ +/* +Use this data source to query list information of api_gateway api_app + +Example Usage + +```hcl +data "tencentcloud_api_gateway_api_apps" "test" { + api_app_id = ["app-rj8t6zx3"] + api_app_name = ["app_test"] +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + apigateway "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/apigateway/v20180808" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func dataSourceTencentCloudAPIGatewayAPIApps() *schema.Resource { + return &schema.Resource{ + Read: dataSourceTencentCloudAPIGatewayAPIAppsRead, + Schema: map[string]*schema.Schema{ + "result_output_file": { + Type: schema.TypeString, + Optional: true, + Description: "Used to save results.", + }, + + "api_app_id": { + Type: schema.TypeString, + Optional: true, + Description: "Api app ID.", + }, + + "api_app_name": { + Type: schema.TypeString, + Optional: true, + Description: "Api app name.", + }, + + "api_app_list": { + Type: schema.TypeList, + Computed: true, + Description: "List of ApiApp.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_app_id": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp ID.", + }, + "api_app_name": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp Name.", + }, + "api_app_key": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp key.", + }, + "api_app_secret": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp secret.", + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp create time.", + }, + "modified_time": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp modified time.", + }, + "api_app_desc": { + Type: schema.TypeString, + Computed: true, + Description: "ApiApp description.", + }, + }, + }, + }, + }, + } +} + +func dataSourceTencentCloudAPIGatewayAPIAppsRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("data_source.tencentcloud_api_gateway_api_apps.read")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiAppId, apiAppName string + apiApps []*apigateway.ApiAppInfo + ) + + if v, ok := d.GetOk("api_app_id"); ok { + apiAppId = v.(string) + } + + if v, ok := d.GetOk("api_app_name"); ok { + apiAppName = v.(string) + } + + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := apiGatewayService.DescribeApiAppList(ctx, apiAppId, apiAppName) + if e != nil { + return retryError(e) + } + apiApps = result + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s read api_gateway apiApps failed, reason:%+v", logId, err) + return err + } + + apiAppList := []interface{}{} + ids := make([]string, 0, len(apiApps)) + if apiApps != nil { + for _, item := range apiApps { + docMap := map[string]interface{}{} + if item.ApiAppId != nil { + docMap["api_app_id"] = item.ApiAppId + } + if item.ApiAppName != nil { + docMap["api_app_name"] = item.ApiAppName + } + if item.ApiAppKey != nil { + docMap["api_app_key"] = item.ApiAppKey + } + if item.ApiAppSecret != nil { + docMap["api_app_secret"] = item.ApiAppSecret + } + if item.CreatedTime != nil { + docMap["created_time"] = item.CreatedTime + } + if item.ModifiedTime != nil { + docMap["modified_time"] = item.ModifiedTime + } + if item.ApiAppDesc != nil { + docMap["api_app_desc"] = item.ApiAppDesc + } + apiAppList = append(apiAppList, docMap) + ids = append(ids, *item.ApiAppId) + } + _ = d.Set("api_app_list", apiAppList) + } + + d.SetId(helper.DataResourceIdsHash(ids)) + output, ok := d.GetOk("result_output_file") + if ok && output.(string) != "" { + if e := writeToFile(output.(string), apiAppList); e != nil { + return e + } + } + + return nil +} diff --git a/tencentcloud/data_source_tc_api_gateway_api_apps_test.go b/tencentcloud/data_source_tc_api_gateway_api_apps_test.go new file mode 100644 index 0000000000..4a76fa579e --- /dev/null +++ b/tencentcloud/data_source_tc_api_gateway_api_apps_test.go @@ -0,0 +1,48 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +var testAPIGatewayAPIAppsResourceName = "data.tencentcloud_api_gateway_api_apps" + +// go test -i; go test -test.run TestAccTencentAPIGatewayAPIAppsDataSource_basic -v +func TestAccTencentAPIGatewayAPIAppsDataSource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAPIGatewayAPIAppDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTestAccTencentAPIGatewayAPIApps(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAPIGatewayAPIAppExists(testAPIGatewayAPIAppResourceName+".test"), + resource.TestCheckResourceAttr(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.#", "1"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.api_app_id"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.api_app_name"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.api_app_key"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.api_app_secret"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.created_time"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.modified_time"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppsResourceName+".test", "api_app_list.0.api_app_desc"), + ), + }, + }, + }) +} + +func testAccTestAccTencentAPIGatewayAPIApps() string { + return ` +resource "tencentcloud_api_gateway_api_app" "test" { + api_app_name = "app_test1" + api_app_desc = "create app desc" +} + +data "tencentcloud_api_gateway_api_apps" "test" { + api_app_id = tencentcloud_api_gateway_api_app.test.id +} +` +} diff --git a/tencentcloud/data_source_tc_api_gateway_api_docs.go b/tencentcloud/data_source_tc_api_gateway_api_docs.go new file mode 100644 index 0000000000..a9055ff24f --- /dev/null +++ b/tencentcloud/data_source_tc_api_gateway_api_docs.go @@ -0,0 +1,114 @@ +/* +Use this data source to query list information of api_gateway api_doc +Example Usage +```hcl +data "tencentcloud_api_gateway_api_docs" "my_api_doc" { +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + apigateway "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/apigateway/v20180808" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func dataSourceTencentCloudAPIGatewayAPIDocs() *schema.Resource { + return &schema.Resource{ + Read: dataSourceTencentCloudAPIGatewayAPIDocsRead, + Schema: map[string]*schema.Schema{ + "result_output_file": { + Type: schema.TypeString, + Optional: true, + Description: "Used to save results.", + }, + + "api_doc_list": { + Type: schema.TypeList, + Computed: true, + Description: "List of ApiDocs.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "api_doc_id": { + Type: schema.TypeString, + Computed: true, + Description: "Api Doc ID.", + }, + "api_doc_name": { + Type: schema.TypeString, + Computed: true, + Description: "Api Doc Name.", + }, + "api_doc_status": { + Type: schema.TypeString, + Computed: true, + Description: "Api Doc Status.", + }, + }, + }, + }, + }, + } +} + +func dataSourceTencentCloudAPIGatewayAPIDocsRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("data_source.tencentcloud_api_gateway_api_docs.read")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiDoc []*apigateway.APIDoc + ) + + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + results, e := apiGatewayService.DescribeApiDocList(ctx) + if e != nil { + return retryError(e) + } + + apiDoc = results + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s read api_gateway apiDocs failed, reason:%+v", logId, err) + return err + } + + apiDocList := []interface{}{} + ids := make([]string, 0, len(apiDoc)) + if apiDoc != nil { + for _, item := range apiDoc { + docMap := map[string]interface{}{} + if item.ApiDocId != nil { + docMap["api_doc_id"] = item.ApiDocId + } + if item.ApiDocName != nil { + docMap["api_doc_name"] = item.ApiDocName + } + if item.ApiDocStatus != nil { + docMap["api_doc_status"] = item.ApiDocStatus + } + apiDocList = append(apiDocList, docMap) + ids = append(ids, *item.ApiDocId) + } + _ = d.Set("api_doc_list", apiDocList) + } + + d.SetId(helper.DataResourceIdsHash(ids)) + output, ok := d.GetOk("result_output_file") + if ok && output.(string) != "" { + if e := writeToFile(output.(string), apiDocList); e != nil { + return e + } + } + + return nil +} diff --git a/tencentcloud/data_source_tc_api_gateway_api_docs_test.go b/tencentcloud/data_source_tc_api_gateway_api_docs_test.go new file mode 100644 index 0000000000..5e2e22caa3 --- /dev/null +++ b/tencentcloud/data_source_tc_api_gateway_api_docs_test.go @@ -0,0 +1,40 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +var testAPIGatewayAPIDocsResourceName = "data.tencentcloud_api_gateway_api_docs" + +// go test -i; go test -test.run TestAccTencentAPIGatewayAPIDocsDataSource_basic -v +func TestAccTencentAPIGatewayAPIDocsDataSource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAPIGatewayAPIDocDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTestAccTencentAPIGatewayAPIDocs, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(testAPIGatewayAPIDocsResourceName+".test", "api_doc_list.#"), + ), + }, + }, + }) +} + +const testAccTestAccTencentAPIGatewayAPIDocs = ` +resource "tencentcloud_api_gateway_api_doc" "test" { + api_doc_name = "doc_test1" + service_id = "service-7lybgojo" + environment = "release" + api_ids = ["api-2bntitvw"] +} + +data "tencentcloud_api_gateway_api_docs" "test" { + depends_on = [tencentcloud_api_gateway_api_doc.test] +} +` diff --git a/tencentcloud/data_source_tc_api_gateway_customer_domains_test.go b/tencentcloud/data_source_tc_api_gateway_customer_domains_test.go index b85ed7dc7d..7df4ff445a 100644 --- a/tencentcloud/data_source_tc_api_gateway_customer_domains_test.go +++ b/tencentcloud/data_source_tc_api_gateway_customer_domains_test.go @@ -8,7 +8,8 @@ import ( var testAPIGatewayDomainSourceName = "data.tencentcloud_api_gateway_customer_domains" -func TestAccTencentAPIGatewayCustomerDomain(t *testing.T) { +// go test -i; go test -test.run TestAccTencentAPIGatewayCustomerDomain_basic -v +func TestAccTencentAPIGatewayCustomerDomain_basic(t *testing.T) { t.Parallel() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -34,7 +35,7 @@ func TestAccTencentAPIGatewayCustomerDomain(t *testing.T) { func testAccTestAccTencentAPIGatewayDomain() string { return ` resource "tencentcloud_api_gateway_custom_domain" "foo" { - service_id = "service-ohxqslqe" + service_id = "service-7lybgojo" sub_domain = "tic-test.dnsv1.com" protocol = "http" net_type = "OUTER" diff --git a/tencentcloud/extension_api_gateway.go b/tencentcloud/extension_api_gateway.go index 6e387daaab..d9b2457641 100644 --- a/tencentcloud/extension_api_gateway.go +++ b/tencentcloud/extension_api_gateway.go @@ -74,6 +74,12 @@ const ( DOMAIN_BIND_SERVICE = "FailedOperation.DomainAlreadyBindService" ) +const ( + API_GATEWAY_API_DOC_STATUS_PROCESSING = "PROCESSING" + API_GATEWAY_API_DOC_STATUS_COMPLETED = "COMPLETED" + API_GATEWAY_API_DOC_STATUS_FAIL = "FAIL" +) + var API_GATEWAY_KEYS = []string{ API_GATEWAY_KEY_ENABLED, API_GATEWAY_KEY_DISABLED, diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 2c2a75ed01..40b5719187 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -89,6 +89,8 @@ API GateWay tencentcloud_api_gateway_customer_domains tencentcloud_api_gateway_usage_plan_environments tencentcloud_api_gateway_api_keys + tencentcloud_api_gateway_api_docs + tencentcloud_api_gateway_api_apps Resource tencentcloud_api_gateway_api @@ -103,6 +105,8 @@ API GateWay tencentcloud_api_gateway_service_release tencentcloud_api_gateway_plugin tencentcloud_api_gateway_plugin_attachment + tencentcloud_api_gateway_api_doc + tencentcloud_api_gateway_api_app Cloud Audit(Audit) Data Source @@ -1500,6 +1504,8 @@ func Provider() *schema.Provider { "tencentcloud_tsf_microservice": dataSourceTencentCloudTsfMicroservice(), "tencentcloud_tsf_unit_rules": dataSourceTencentCloudTsfUnitRules(), "tencentcloud_lighthouse_bundle": dataSourceTencentCloudLighthouseBundle(), + "tencentcloud_api_gateway_api_docs": dataSourceTencentCloudAPIGatewayAPIDocs(), + "tencentcloud_api_gateway_api_apps": dataSourceTencentCloudAPIGatewayAPIApps(), }, ResourcesMap: map[string]*schema.Resource{ @@ -1983,6 +1989,8 @@ func Provider() *schema.Provider { "tencentcloud_lighthouse_key_pair": resourceTencentCloudLighthouseKeyPair(), "tencentcloud_lighthouse_snapshot": resourceTencentCloudLighthouseSnapshot(), "tencentcloud_lighthouse_apply_instance_snapshot": resourceTencentCloudLighthouseApplyInstanceSnapshot(), + "tencentcloud_api_gateway_api_doc": resourceTencentCloudAPIGatewayAPIDoc(), + "tencentcloud_api_gateway_api_app": resourceTencentCloudAPIGatewayAPIApp(), }, ConfigureFunc: providerConfigure, diff --git a/tencentcloud/resource_tc_api_gateway_api_app.go b/tencentcloud/resource_tc_api_gateway_api_app.go new file mode 100644 index 0000000000..2712ff23d5 --- /dev/null +++ b/tencentcloud/resource_tc_api_gateway_api_app.go @@ -0,0 +1,239 @@ +/* +Provides a resource to create a APIGateway ApiApp + +Example Usage + +```hcl +resource "tencentcloud_api_gateway_api_app" "my_api_app" { + api_app_name = "app_test1" + api_app_desc = "app desc." +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + apiGateway "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/apigateway/v20180808" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudAPIGatewayAPIApp() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudAPIGatewayAPIAppCreate, + Read: resourceTencentCloudAPIGatewayAPIAppRead, + Update: resourceTencentCloudAPIGatewayAPIAppUpdate, + Delete: resourceTencentCloudAPIGatewayAPIAppDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "api_app_name": { + Type: schema.TypeString, + Required: true, + Description: "Api app name.", + }, + "api_app_desc": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "App description.", + }, + "api_app_id": { + Type: schema.TypeString, + Computed: true, + Description: "Api app ID.", + }, + "api_app_key": { + Type: schema.TypeString, + Computed: true, + Description: "Api app key.", + }, + "api_app_secret": { + Type: schema.TypeString, + Computed: true, + Description: "Api app secret.", + }, + "created_time": { + Type: schema.TypeString, + Computed: true, + Description: "Api app created time.", + }, + "modified_time": { + Type: schema.TypeString, + Computed: true, + Description: "Api app modified time.", + }, + }, + } +} + +func resourceTencentCloudAPIGatewayAPIAppCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_app.create")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + request = apiGateway.NewCreateApiAppRequest() + response *apiGateway.CreateApiAppResponse + apiAppId string + err error + ) + + if v, ok := d.GetOk("api_app_name"); ok { + request.ApiAppName = helper.String(v.(string)) + } + + if v, ok := d.GetOk("api_app_desc"); ok { + request.ApiAppDesc = helper.String(v.(string)) + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, err := meta.(*TencentCloudClient).apiV3Conn.UseAPIGatewayClient().CreateApiApp(request) + if err != nil { + return retryError(err) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + response = result + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s create api_app failed, reason:%+v", logId, err) + return err + } + + apiAppId = *response.Response.Result.ApiAppId + d.SetId(apiAppId) + return resourceTencentCloudAPIGatewayAPIAppRead(d, meta) +} + +func resourceTencentCloudAPIGatewayAPIAppRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_app.read")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiAppId = d.Id() + apiAppInfo *apiGateway.ApiAppInfos + err error + ) + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + apiAppInfo, err = apiGatewayService.DescribeApiApp(ctx, apiAppId) + if err != nil { + return retryError(err) + } + return nil + }) + + if apiAppInfo == nil { + d.SetId("") + log.Printf("resource `api_app` %s does not exist", apiAppId) + return nil + } + + apiAppData := apiAppInfo.ApiAppSet[0] + if apiAppData.ApiAppId != nil { + _ = d.Set("api_app_id", apiAppData.ApiAppId) + } + + if apiAppData.ApiAppName != nil { + _ = d.Set("api_app_name", apiAppData.ApiAppName) + } + + if apiAppData.ApiAppKey != nil { + _ = d.Set("api_app_key", apiAppData.ApiAppKey) + } + + if apiAppData.ApiAppSecret != nil { + _ = d.Set("api_app_secret", apiAppData.ApiAppSecret) + } + + if apiAppData.CreatedTime != nil { + _ = d.Set("created_time", apiAppData.CreatedTime) + } + + if apiAppData.ModifiedTime != nil { + _ = d.Set("modified_time", apiAppData.ModifiedTime) + } + + if apiAppData.ApiAppDesc != nil { + err = d.Set("api_app_desc", apiAppData.ApiAppDesc) + if err != nil { + return err + } + } + + return nil +} + +func resourceTencentCloudAPIGatewayAPIAppUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_app.update")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + request = apiGateway.NewModifyApiAppRequest() + apiAppId = d.Id() + err error + ) + + request.ApiAppId = &apiAppId + if d.HasChange("api_app_name") { + if v, ok := d.GetOk("api_app_name"); ok { + request.ApiAppName = helper.String(v.(string)) + } + } + + if d.HasChange("api_app_desc") { + if v, ok := d.GetOk("api_app_desc"); ok { + request.ApiAppDesc = helper.String(v.(string)) + } + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseAPIGatewayClient().ModifyApiApp(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s update api_app failed, reason:%+v", logId, err) + return err + } + + return resourceTencentCloudAPIGatewayAPIAppRead(d, meta) +} + +func resourceTencentCloudAPIGatewayAPIAppDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_app.delete")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiAppId = d.Id() + err error + ) + + if err = apiGatewayService.DeleteAPIGatewayAPIAppById(ctx, apiAppId); err != nil { + return err + } + + return nil +} diff --git a/tencentcloud/resource_tc_api_gateway_api_app_test.go b/tencentcloud/resource_tc_api_gateway_api_app_test.go new file mode 100644 index 0000000000..d897220df0 --- /dev/null +++ b/tencentcloud/resource_tc_api_gateway_api_app_test.go @@ -0,0 +1,121 @@ +package tencentcloud + +import ( + "context" + "fmt" + "testing" + + sdkErrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var testAPIGatewayAPIAppResourceName = "tencentcloud_api_gateway_api_app" +var testAPIGatewayAPIAppResourceKey = testAPIGatewayAPIAppResourceName + ".test" + +// go test -i; go test -test.run TestAccTencentCloudAPIGateWayAPIAppResource_basic -v +func TestAccTencentCloudAPIGateWayAPIAppResource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAPIGatewayAPIAppDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAPIGatewayAPIApp, + Check: resource.ComposeTestCheckFunc( + testAccCheckAPIGatewayAPIAppExists(testAPIGatewayAPIAppResourceKey), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIAppResourceKey, "api_app_name"), + resource.TestCheckResourceAttr(testAPIGatewayAPIAppResourceKey, "api_app_desc", "app desc"), + ), + }, + { + ResourceName: testAPIGatewayAPIAppResourceKey, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAPIGatewayAPIAppUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckAPIGatewayAPIAppExists(testAPIGatewayAPIAppResourceKey), + resource.TestCheckResourceAttr(testAPIGatewayAPIAppResourceKey, "api_app_name", "update_app_name_test"), + resource.TestCheckResourceAttr(testAPIGatewayAPIAppResourceKey, "api_app_desc", "update app desc"), + ), + }, + }, + }) +} + +func testAccCheckAPIGatewayAPIAppDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != testAPIGatewayAPIAppResourceName { + continue + } + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + service := APIGatewayService{client: testAccProvider.Meta().(*TencentCloudClient).apiV3Conn} + + apiApp, err := service.DescribeApiApp(ctx, rs.Primary.ID) + if err != nil { + if sdkerr, ok := err.(*sdkErrors.TencentCloudSDKError); ok { + if sdkerr.Code == "InvalidParameterValue.InvalidCommandId" { + return nil + } + } + return err + } + + if apiApp != nil { + if *apiApp.TotalCount == 0 { + return nil + } + return fmt.Errorf("api_gateway api_app %s still exists", rs.Primary.ID) + } + } + return nil +} + +func testAccCheckAPIGatewayAPIAppExists(r string) resource.TestCheckFunc { + return func(s *terraform.State) error { + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("resource %s is not found", r) + } + + service := APIGatewayService{client: testAccProvider.Meta().(*TencentCloudClient).apiV3Conn} + command, err := service.DescribeApiApp(ctx, rs.Primary.ID) + if err != nil { + if sdkerr, ok := err.(*sdkErrors.TencentCloudSDKError); ok { + if sdkerr.Code == "InvalidParameterValue.InvalidCommandId" { + return nil + } + } + return err + } + + if command == nil { + return fmt.Errorf("api_gateway api_app %s is not found", rs.Primary.ID) + } + + return nil + } +} + +const testAccAPIGatewayAPIApp = ` +resource "tencentcloud_api_gateway_api_app" "test" { + api_app_name = "create_app_test" + api_app_desc = "app desc" +} +` + +const testAccAPIGatewayAPIAppUpdate = ` +resource "tencentcloud_api_gateway_api_app" "test" { + api_app_name = "update_app_name_test" + api_app_desc = "update app desc" +} +` diff --git a/tencentcloud/resource_tc_api_gateway_api_doc.go b/tencentcloud/resource_tc_api_gateway_api_doc.go new file mode 100644 index 0000000000..d817672352 --- /dev/null +++ b/tencentcloud/resource_tc_api_gateway_api_doc.go @@ -0,0 +1,385 @@ +/* +Provides a resource to create a APIGateway ApiDoc + +Example Usage + +```hcl +resource "tencentcloud_api_gateway_api_doc" "my_api_doc" { + api_doc_name = "doc_test1" + service_id = "service_test1" + environment = "release" + api_ids = ["api-test1", "api-test2"] +} +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + apiGateway "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/apigateway/v20180808" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudAPIGatewayAPIDoc() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudAPIGatewayAPIDocCreate, + Read: resourceTencentCloudAPIGatewayAPIDocRead, + Update: resourceTencentCloudAPIGatewayAPIDocUpdate, + Delete: resourceTencentCloudAPIGatewayAPIDocDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "api_doc_name": { + Type: schema.TypeString, + Required: true, + Description: "Api Document name.", + }, + "service_id": { + Type: schema.TypeString, + Required: true, + Description: "Service name.", + }, + "environment": { + Type: schema.TypeString, + Required: true, + Description: "Env name.", + }, + "api_ids": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of APIs for generating documents.", + }, + "api_doc_id": { + Type: schema.TypeString, + Computed: true, + Description: "Api Document ID.", + }, + "api_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Api Document count.", + }, + "view_count": { + Type: schema.TypeInt, + Computed: true, + Description: "API Document Viewing Times.", + }, + "release_count": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of API document releases.", + }, + "api_doc_uri": { + Type: schema.TypeString, + Computed: true, + Description: "API Document Access URI.", + }, + "share_password": { + Type: schema.TypeString, + Computed: true, + Description: "API Document Sharing Password.", + }, + "api_doc_status": { + Type: schema.TypeString, + Computed: true, + Description: "API Document Build Status.", + }, + "updated_time": { + Type: schema.TypeString, + Computed: true, + Description: "API Document update time.", + }, + "service_name": { + Type: schema.TypeString, + Computed: true, + Description: "API Document service name.", + }, + "api_names": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "List of names for generating documents.", + }, + }, + } +} + +func resourceTencentCloudAPIGatewayAPIDocCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_doc.create")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + request = apiGateway.NewCreateAPIDocRequest() + response *apiGateway.CreateAPIDocResponse + apiDocId string + err error + ) + + if v, ok := d.GetOk("api_doc_name"); ok { + request.ApiDocName = helper.String(v.(string)) + } + + if v, ok := d.GetOk("service_id"); ok { + request.ServiceId = helper.String(v.(string)) + } + + if v, ok := d.GetOk("environment"); ok { + request.Environment = helper.String(v.(string)) + } + + if v, ok := d.GetOk("api_ids"); ok { + for _, item := range v.(*schema.Set).List() { + id := helper.String(item.(string)) + request.ApiIds = append(request.ApiIds, id) + } + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, err := meta.(*TencentCloudClient).apiV3Conn.UseAPIGatewayClient().CreateAPIDoc(request) + if err != nil { + return retryError(err) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + response = result + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s create api_doc failed, reason:%+v", logId, err) + return err + } + + apiDocId = *response.Response.Result.ApiDocId + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + apiDocInfo, err := apiGatewayService.DescribeApiDoc(ctx, apiDocId) + if err != nil { + return retryError(err) + } + if apiDocInfo == nil { + err = fmt.Errorf("api_doc_id %s not exists", apiDocId) + return resource.NonRetryableError(err) + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_PROCESSING { + return resource.RetryableError(fmt.Errorf("create api_doc task status is %s", API_GATEWAY_API_DOC_STATUS_PROCESSING)) + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_COMPLETED { + return nil + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_FAIL { + return resource.NonRetryableError(fmt.Errorf("create api_doc task status is %s", API_GATEWAY_API_DOC_STATUS_PROCESSING)) + } + err = fmt.Errorf("create api_doc task status is %v, we won't wait for it finish", *apiDocInfo.ApiDocStatus) + return resource.NonRetryableError(err) + }) + + if err != nil { + log.Printf("[CRITAL]%s create api_doc task fail, reason:%s\n ", logId, err.Error()) + return err + } + + d.SetId(apiDocId) + + return resourceTencentCloudAPIGatewayAPIDocRead(d, meta) +} + +func resourceTencentCloudAPIGatewayAPIDocRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_doc.read")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiDocId = d.Id() + apiDocInfo *apiGateway.APIDocInfo + err error + ) + + apiDocInfo, err = apiGatewayService.DescribeApiDoc(ctx, apiDocId) + if err != nil { + return err + } + + if apiDocInfo == nil { + d.SetId("") + return nil + } + + if apiDocInfo.ApiDocId != nil { + _ = d.Set("api_doc_id", apiDocInfo.ApiDocId) + } + + if apiDocInfo.ApiDocName != nil { + _ = d.Set("api_doc_name", apiDocInfo.ApiDocName) + } + + if apiDocInfo.ApiCount != nil { + _ = d.Set("api_count", apiDocInfo.ApiCount) + } + + if apiDocInfo.ViewCount != nil { + _ = d.Set("view_count", apiDocInfo.ViewCount) + } + + if apiDocInfo.ReleaseCount != nil { + _ = d.Set("release_count", apiDocInfo.ReleaseCount) + } + + if apiDocInfo.ApiDocUri != nil { + _ = d.Set("api_doc_uri", apiDocInfo.ApiDocUri) + } + + if apiDocInfo.SharePassword != nil { + _ = d.Set("share_password", apiDocInfo.SharePassword) + } + + if apiDocInfo.ApiDocStatus != nil { + _ = d.Set("api_doc_status", apiDocInfo.ApiDocStatus) + } + + if apiDocInfo.UpdatedTime != nil { + _ = d.Set("updated_time", apiDocInfo.UpdatedTime) + } + + if apiDocInfo.ServiceId != nil { + _ = d.Set("service_id", apiDocInfo.ServiceId) + } + + if apiDocInfo.ServiceName != nil { + _ = d.Set("service_name", apiDocInfo.ServiceName) + } + + if apiDocInfo.ApiIds != nil { + apiIdsList := []interface{}{} + for _, id := range apiDocInfo.ApiIds { + apiIdsList = append(apiIdsList, id) + } + _ = d.Set("api_ids", apiIdsList) + } + + if apiDocInfo.ApiNames != nil { + apiNameList := []interface{}{} + for _, name := range apiDocInfo.ApiNames { + apiNameList = append(apiNameList, name) + } + _ = d.Set("api_names", apiNameList) + } + + if apiDocInfo.Environment != nil { + _ = d.Set("environment", apiDocInfo.Environment) + } + + return nil +} + +func resourceTencentCloudAPIGatewayAPIDocUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_doc.update")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + request = apiGateway.NewModifyAPIDocRequest() + apiDocId = d.Id() + err error + ) + + request.ApiDocId = &apiDocId + + if v, ok := d.GetOk("api_doc_name"); ok { + request.ApiDocName = helper.String(v.(string)) + } + + if v, ok := d.GetOk("service_id"); ok { + request.ServiceId = helper.String(v.(string)) + } + + if v, ok := d.GetOk("environment"); ok { + request.Environment = helper.String(v.(string)) + } + + if v, ok := d.GetOk("api_ids"); ok { + for _, item := range v.(*schema.Set).List() { + id := helper.String(item.(string)) + request.ApiIds = append(request.ApiIds, id) + } + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseAPIGatewayClient().ModifyAPIDoc(request) + if e != nil { + return retryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + return nil + }) + + if err != nil { + log.Printf("[CRITAL]%s update api_doc failed, reason:%+v", logId, err) + return err + } + + err = resource.Retry(writeRetryTimeout, func() *resource.RetryError { + apiDocInfo, err := apiGatewayService.DescribeApiDoc(ctx, apiDocId) + if err != nil { + return retryError(err) + } + if apiDocInfo == nil { + err = fmt.Errorf("api_doc_id %s not exists", apiDocId) + return resource.NonRetryableError(err) + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_PROCESSING { + return resource.RetryableError(fmt.Errorf("create api_doc task status is %s", API_GATEWAY_API_DOC_STATUS_PROCESSING)) + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_COMPLETED { + return nil + } + if *apiDocInfo.ApiDocStatus == API_GATEWAY_API_DOC_STATUS_FAIL { + return resource.NonRetryableError(fmt.Errorf("create api_doc task status is %s", API_GATEWAY_API_DOC_STATUS_PROCESSING)) + } + err = fmt.Errorf("create api_doc task status is %v, we won't wait for it finish", *apiDocInfo.ApiDocStatus) + return resource.NonRetryableError(err) + }) + + if err != nil { + log.Printf("[CRITAL]%s update api_doc task fail, reason:%s\n ", logId, err.Error()) + return err + } + + return resourceTencentCloudAPIGatewayAPIDocRead(d, meta) +} + +func resourceTencentCloudAPIGatewayAPIDocDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_api_gateway_api_doc.delete")() + defer inconsistentCheck(d, meta)() + + var ( + logId = getLogId(contextNil) + ctx = context.WithValue(context.TODO(), logIdKey, logId) + apiGatewayService = APIGatewayService{client: meta.(*TencentCloudClient).apiV3Conn} + apiDocId = d.Id() + err error + ) + + if err = apiGatewayService.DeleteAPIGatewayAPIDocById(ctx, apiDocId); err != nil { + return err + } + + return nil +} diff --git a/tencentcloud/resource_tc_api_gateway_api_doc_test.go b/tencentcloud/resource_tc_api_gateway_api_doc_test.go new file mode 100644 index 0000000000..bf2025925d --- /dev/null +++ b/tencentcloud/resource_tc_api_gateway_api_doc_test.go @@ -0,0 +1,122 @@ +package tencentcloud + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + sdkErrors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" +) + +var testAPIGatewayAPIDocResourceName = "tencentcloud_api_gateway_api_doc" +var testAPIGatewayAPIDocResourceKey = testAPIGatewayAPIDocResourceName + ".test" + +// go test -i; go test -test.run TestAccTencentCloudAPIGateWayAPIDocResource_basic -v +func TestAccTencentCloudAPIGateWayAPIDocResource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAPIGatewayAPIDocDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAPIGatewayAPIDoc, + Check: resource.ComposeTestCheckFunc( + testAccCheckAPIGatewayAPIDocExists(testAPIGatewayAPIDocResourceKey), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIDocResourceKey, "api_doc_name"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIDocResourceKey, "service_id"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIDocResourceKey, "environment"), + resource.TestCheckResourceAttrSet(testAPIGatewayAPIDocResourceKey, "api_ids.#"), + ), + }, + { + ResourceName: testAPIGatewayAPIDocResourceKey, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAPIGatewayAPIDocUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckAPIGatewayAPIDocExists(testAPIGatewayAPIDocResourceKey), + resource.TestCheckResourceAttr(testAPIGatewayAPIDocResourceKey, "api_doc_name", "update_doc_name_test"), + ), + }, + }, + }) +} + +func testAccCheckAPIGatewayAPIDocDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != testAPIGatewayAPIDocResourceName { + continue + } + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + service := APIGatewayService{client: testAccProvider.Meta().(*TencentCloudClient).apiV3Conn} + + apiDoc, err := service.DescribeApiDoc(ctx, rs.Primary.ID) + if err != nil { + if sdkerr, ok := err.(*sdkErrors.TencentCloudSDKError); ok { + if sdkerr.Code == "ResourceNotFound.InvalidApiDoc" || sdkerr.Code == "InvalidParameterValue.InvalidCommandId" { + return nil + } + } + return err + } + + if apiDoc != nil { + return fmt.Errorf("api_gateway api_doc %s still exists", rs.Primary.ID) + } + } + return nil +} + +func testAccCheckAPIGatewayAPIDocExists(r string) resource.TestCheckFunc { + return func(s *terraform.State) error { + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + + rs, ok := s.RootModule().Resources[r] + if !ok { + return fmt.Errorf("resource %s is not found", r) + } + + service := APIGatewayService{client: testAccProvider.Meta().(*TencentCloudClient).apiV3Conn} + command, err := service.DescribeApiDoc(ctx, rs.Primary.ID) + if err != nil { + if sdkerr, ok := err.(*sdkErrors.TencentCloudSDKError); ok { + if sdkerr.Code == "ResourceNotFound.InvalidApiDoc" || sdkerr.Code == "InvalidParameterValue.InvalidCommandId" { + return nil + } + } + return err + } + + if command == nil { + return fmt.Errorf("api_gateway api_doc %s is not found", rs.Primary.ID) + } + + return nil + } +} + +const testAccAPIGatewayAPIDoc = ` +resource "tencentcloud_api_gateway_api_doc" "test" { + api_doc_name = "doc_test1" + service_id = "service-7lybgojo" + environment = "release" + api_ids = ["api-2bntitvw"] +} +` + +const testAccAPIGatewayAPIDocUpdate = ` +resource "tencentcloud_api_gateway_api_doc" "test" { + api_doc_name = "update_doc_name_test" + service_id = "service-7lybgojo" + environment = "release" + api_ids = ["api-2bntitvw"] +} +` diff --git a/tencentcloud/resource_tc_api_gateway_custom_domain.go b/tencentcloud/resource_tc_api_gateway_custom_domain.go index 5e57ed89d6..a67a7f512c 100644 --- a/tencentcloud/resource_tc_api_gateway_custom_domain.go +++ b/tencentcloud/resource_tc_api_gateway_custom_domain.go @@ -83,6 +83,12 @@ func resourceTencentCloudAPIGatewayCustomDomain() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Description: "Custom domain name path mapping. The data format is: `path#environment`. Optional values for the environment are `test`, `prepub`, and `release`.", }, + "is_forced_https": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether to force HTTP requests to jump to HTTPS, default to false. When the parameter is true, the API gateway will redirect all HTTP protocol requests using the custom domain name to the HTTPS protocol for forwarding.", + }, //compute "status": { Type: schema.TypeInt, @@ -106,6 +112,7 @@ func resourceTencentCloudAPIGatewayCustomDomainCreate(d *schema.ResourceData, me netType = d.Get("net_type").(string) defaultDomain = d.Get("default_domain").(string) isDefaultMapping = d.Get("is_default_mapping").(bool) + isForcedHttps = d.Get("is_forced_https").(bool) certificateId string pathMappings []string err error @@ -118,7 +125,7 @@ func resourceTencentCloudAPIGatewayCustomDomainCreate(d *schema.ResourceData, me pathMappings = helper.InterfacesStrings(v.(*schema.Set).List()) } - err = apiGatewayService.BindSubDomainService(ctx, serviceId, subDomain, protocol, netType, defaultDomain, isDefaultMapping, certificateId, pathMappings) + err = apiGatewayService.BindSubDomainService(ctx, serviceId, subDomain, protocol, netType, defaultDomain, isDefaultMapping, certificateId, pathMappings, isForcedHttps) if err != nil { return err } @@ -173,6 +180,7 @@ func resourceTencentCloudAPIGatewayCustomDomainRead(d *schema.ResourceData, meta _ = d.Set("protocol", resultInfo.Protocol) _ = d.Set("net_type", resultInfo.NetType) _ = d.Set("service_id", serviceId) + _ = d.Set("is_forced_https", resultInfo.IsForcedHttps) return nil } @@ -191,6 +199,7 @@ func resourceTencentCloudAPIGatewayCustomDomainUpdate(d *schema.ResourceData, me protocol string netType string pathMappings []string + isForcedHttps bool hasChange bool ) @@ -230,12 +239,18 @@ func resourceTencentCloudAPIGatewayCustomDomainUpdate(d *schema.ResourceData, me if v, ok := d.GetOk("path_mappings"); ok { pathMappings = helper.InterfacesStrings(v.(*schema.Set).List()) } + if d.HasChange("path_mappings") { hasChange = true } + isForcedHttps = d.Get("is_forced_https").(bool) + if d.HasChange("is_forced_https") { + hasChange = true + } + if hasChange { - err := apiGatewayService.ModifySubDomainService(ctx, serviceId, subDomain, isDefaultMapping, certificateId, protocol, netType, pathMappings) + err := apiGatewayService.ModifySubDomainService(ctx, serviceId, subDomain, isDefaultMapping, certificateId, protocol, netType, pathMappings, isForcedHttps) if err != nil { return err } diff --git a/tencentcloud/resource_tc_api_gateway_custom_domain_test.go b/tencentcloud/resource_tc_api_gateway_custom_domain_test.go index 470f8d6f5e..8db89cfb22 100644 --- a/tencentcloud/resource_tc_api_gateway_custom_domain_test.go +++ b/tencentcloud/resource_tc_api_gateway_custom_domain_test.go @@ -10,7 +10,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccTencentCloudAPIGateWayCustomDomain(t *testing.T) { +// go test -i; go test -test.run TestAccTencentCloudAPIGateWayCustomDomain_basic -v +func TestAccTencentCloudAPIGateWayCustomDomain_basic(t *testing.T) { t.Parallel() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, diff --git a/tencentcloud/service_tencentcloud_api_gateway.go b/tencentcloud/service_tencentcloud_api_gateway.go index 1bb1dfb83f..aa22d5590b 100644 --- a/tencentcloud/service_tencentcloud_api_gateway.go +++ b/tencentcloud/service_tencentcloud_api_gateway.go @@ -1114,7 +1114,7 @@ func (me *APIGatewayService) ModifyServiceEnvironmentStrategy(ctx context.Contex } func (me *APIGatewayService) BindSubDomainService(ctx context.Context, - serviceId, subDomain, protocol, netType, defaultDomain string, isDefaultMapping bool, certificateId string, pathMappings []string) (errRet error) { + serviceId, subDomain, protocol, netType, defaultDomain string, isDefaultMapping bool, certificateId string, pathMappings []string, isForcedHttps bool) (errRet error) { var ( request = apigateway.NewBindSubDomainRequest() err error @@ -1126,6 +1126,7 @@ func (me *APIGatewayService) BindSubDomainService(ctx context.Context, request.NetType = &netType request.NetSubDomain = &defaultDomain request.IsDefaultMapping = &isDefaultMapping + request.IsForcedHttps = &isForcedHttps if certificateId != "" { request.CertificateId = &certificateId } @@ -1231,7 +1232,7 @@ func (me *APIGatewayService) DescribeServiceSubDomainMappings(ctx context.Contex } func (me *APIGatewayService) ModifySubDomainService(ctx context.Context, - serviceId, subDomain string, isDefaultMapping bool, certificateId, protocol, netType string, pathMappings []string) (errRet error) { + serviceId, subDomain string, isDefaultMapping bool, certificateId, protocol, netType string, pathMappings []string, isForcedHttps bool) (errRet error) { var ( request = apigateway.NewModifySubDomainRequest() response *apigateway.ModifySubDomainResponse @@ -1241,6 +1242,7 @@ func (me *APIGatewayService) ModifySubDomainService(ctx context.Context, request.ServiceId = &serviceId request.SubDomain = &subDomain request.IsDefaultMapping = &isDefaultMapping + request.IsForcedHttps = &isForcedHttps if certificateId != "" { request.CertificateId = &certificateId } @@ -1700,3 +1702,227 @@ func (me *APIGatewayService) DeleteApiGatewayPluginAttachmentById(ctx context.Co return } + +func (me *APIGatewayService) DescribeApiDoc(ctx context.Context, apiDocId string) (apiDoc *apigateway.APIDocInfo, errRet error) { + var ( + logId = getLogId(ctx) + request = apigateway.NewDescribeAPIDocDetailRequest() + ) + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "query object", request.ToJsonString(), errRet.Error()) + } + }() + + request.ApiDocId = &apiDocId + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DescribeAPIDocDetail(request) + if err != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), err.Error()) + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + if response == nil { + return + } + + apiDoc = response.Response.Result + return +} + +func (me *APIGatewayService) DescribeApiDocList(ctx context.Context) (apiDoc []*apigateway.APIDoc, errRet error) { + var ( + logId = getLogId(ctx) + request = apigateway.NewDescribeAPIDocsRequest() + ) + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "query object", request.ToJsonString(), errRet.Error()) + } + }() + + var offset int64 = 0 + var pageSize int64 = 100 + + for { + request.Offset = &offset + request.Limit = &pageSize + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DescribeAPIDocs(request) + if err != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), err.Error()) + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + if response == nil || *response.Response.Result.TotalCount == 0 { + break + } + + apiDoc = append(apiDoc, response.Response.Result.APIDocSet...) + if *response.Response.Result.TotalCount < pageSize { + break + } + + offset += pageSize + } + return +} + +func (me *APIGatewayService) DeleteAPIGatewayAPIDocById(ctx context.Context, apiDocId string) (errRet error) { + logId := getLogId(ctx) + request := apigateway.NewDeleteAPIDocRequest() + request.ApiDocId = &apiDocId + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "delete object", request.ToJsonString(), errRet.Error()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DeleteAPIDoc(request) + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + return +} + +func (me *APIGatewayService) DescribeApiApp(ctx context.Context, apiAppId string) (apiDoc *apigateway.ApiAppInfos, errRet error) { + var ( + logId = getLogId(ctx) + request = apigateway.NewDescribeApiAppsStatusRequest() + ) + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "query object", request.ToJsonString(), errRet.Error()) + } + }() + + request.Filters = []*apigateway.Filter{ + { + Name: helper.String("ApiAppId"), + Values: helper.Strings([]string{apiAppId}), + }, + } + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DescribeApiAppsStatus(request) + if err != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), err.Error()) + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + if response == nil || *response.Response.Result.TotalCount == 0 { + return + } + + apiDoc = response.Response.Result + return +} + +func (me *APIGatewayService) DescribeApiAppList(ctx context.Context, apiAppId, apiAppName string) (apiApp []*apigateway.ApiAppInfo, errRet error) { + var ( + logId = getLogId(ctx) + request = apigateway.NewDescribeApiAppsStatusRequest() + ) + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "query object", request.ToJsonString(), errRet.Error()) + } + }() + + request.Filters = []*apigateway.Filter{} + if apiAppId != "" { + request.Filters = append(request.Filters, + &apigateway.Filter{ + Name: helper.String("ApiAppId"), + Values: helper.Strings([]string{apiAppId}), + }) + } + + if apiAppName != "" { + request.Filters = append(request.Filters, + &apigateway.Filter{ + Name: helper.String("ApiAppName"), + Values: helper.Strings([]string{apiAppName}), + }) + } + + var offset int64 = 0 + var pageSize int64 = 100 + + for { + request.Offset = &offset + request.Limit = &pageSize + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DescribeApiAppsStatus(request) + if err != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), err.Error()) + errRet = err + return + } + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + if response == nil || *response.Response.Result.TotalCount == 0 { + break + } + apiApp = append(apiApp, response.Response.Result.ApiAppSet...) + if *response.Response.Result.TotalCount < pageSize { + break + } + offset += pageSize + } + return +} + +func (me *APIGatewayService) DeleteAPIGatewayAPIAppById(ctx context.Context, apiAppId string) (errRet error) { + logId := getLogId(ctx) + request := apigateway.NewDeleteApiAppRequest() + request.ApiAppId = &apiAppId + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, "delete object", request.ToJsonString(), errRet.Error()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseAPIGatewayClient().DeleteApiApp(request) + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + return +} diff --git a/website/docs/d/api_gateway_api_apps.html.markdown b/website/docs/d/api_gateway_api_apps.html.markdown new file mode 100644 index 0000000000..70a5a95b8b --- /dev/null +++ b/website/docs/d/api_gateway_api_apps.html.markdown @@ -0,0 +1,44 @@ +--- +subcategory: "API GateWay" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_api_gateway_api_apps" +sidebar_current: "docs-tencentcloud-datasource-api_gateway_api_apps" +description: |- + Use this data source to query list information of api_gateway api_app +--- + +# tencentcloud_api_gateway_api_apps + +Use this data source to query list information of api_gateway api_app + +## Example Usage + +```hcl +data "tencentcloud_api_gateway_api_apps" "test" { + api_app_id = ["app-rj8t6zx3"] + api_app_name = ["app_test"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `api_app_id` - (Optional, String) Api app ID. +* `api_app_name` - (Optional, String) Api app name. +* `result_output_file` - (Optional, String) Used to save results. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `api_app_list` - List of ApiApp. + * `api_app_desc` - ApiApp description. + * `api_app_id` - ApiApp ID. + * `api_app_key` - ApiApp key. + * `api_app_name` - ApiApp Name. + * `api_app_secret` - ApiApp secret. + * `created_time` - ApiApp create time. + * `modified_time` - ApiApp modified time. + + diff --git a/website/docs/d/api_gateway_api_docs.html.markdown b/website/docs/d/api_gateway_api_docs.html.markdown new file mode 100644 index 0000000000..f4a3715cf9 --- /dev/null +++ b/website/docs/d/api_gateway_api_docs.html.markdown @@ -0,0 +1,36 @@ +--- +subcategory: "API GateWay" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_api_gateway_api_docs" +sidebar_current: "docs-tencentcloud-datasource-api_gateway_api_docs" +description: |- + Use this data source to query list information of api_gateway api_doc +--- + +# tencentcloud_api_gateway_api_docs + +Use this data source to query list information of api_gateway api_doc + +## Example Usage + +```hcl +data "tencentcloud_api_gateway_api_docs" "my_api_doc" { +} +``` + +## Argument Reference + +The following arguments are supported: + +* `result_output_file` - (Optional, String) Used to save results. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `api_doc_list` - List of ApiDocs. + * `api_doc_id` - Api Doc ID. + * `api_doc_name` - Api Doc Name. + * `api_doc_status` - Api Doc Status. + + diff --git a/website/docs/r/api_gateway_api_app.html.markdown b/website/docs/r/api_gateway_api_app.html.markdown new file mode 100644 index 0000000000..f205b2a25c --- /dev/null +++ b/website/docs/r/api_gateway_api_app.html.markdown @@ -0,0 +1,41 @@ +--- +subcategory: "API GateWay" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_api_gateway_api_app" +sidebar_current: "docs-tencentcloud-resource-api_gateway_api_app" +description: |- + Provides a resource to create a APIGateway ApiApp +--- + +# tencentcloud_api_gateway_api_app + +Provides a resource to create a APIGateway ApiApp + +## Example Usage + +```hcl +resource "tencentcloud_api_gateway_api_app" "my_api_app" { + api_app_name = "app_test1" + api_app_desc = "app desc." +} +``` + +## Argument Reference + +The following arguments are supported: + +* `api_app_name` - (Required, String) Api app name. +* `api_app_desc` - (Optional, String) App description. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `api_app_id` - Api app ID. +* `api_app_key` - Api app key. +* `api_app_secret` - Api app secret. +* `created_time` - Api app created time. +* `modified_time` - Api app modified time. + + diff --git a/website/docs/r/api_gateway_api_doc.html.markdown b/website/docs/r/api_gateway_api_doc.html.markdown new file mode 100644 index 0000000000..10f47ed320 --- /dev/null +++ b/website/docs/r/api_gateway_api_doc.html.markdown @@ -0,0 +1,50 @@ +--- +subcategory: "API GateWay" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_api_gateway_api_doc" +sidebar_current: "docs-tencentcloud-resource-api_gateway_api_doc" +description: |- + Provides a resource to create a APIGateway ApiDoc +--- + +# tencentcloud_api_gateway_api_doc + +Provides a resource to create a APIGateway ApiDoc + +## Example Usage + +```hcl +resource "tencentcloud_api_gateway_api_doc" "my_api_doc" { + api_doc_name = "doc_test1" + service_id = "service_test1" + environment = "release" + api_ids = ["api-test1", "api-test2"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `api_doc_name` - (Required, String) Api Document name. +* `api_ids` - (Required, Set: [`String`]) List of APIs for generating documents. +* `environment` - (Required, String) Env name. +* `service_id` - (Required, String) Service name. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `api_count` - Api Document count. +* `api_doc_id` - Api Document ID. +* `api_doc_status` - API Document Build Status. +* `api_doc_uri` - API Document Access URI. +* `api_names` - List of names for generating documents. +* `release_count` - Number of API document releases. +* `service_name` - API Document service name. +* `share_password` - API Document Sharing Password. +* `updated_time` - API Document update time. +* `view_count` - API Document Viewing Times. + + diff --git a/website/docs/r/api_gateway_custom_domain.html.markdown b/website/docs/r/api_gateway_custom_domain.html.markdown index 08604db5ca..569e617187 100644 --- a/website/docs/r/api_gateway_custom_domain.html.markdown +++ b/website/docs/r/api_gateway_custom_domain.html.markdown @@ -36,6 +36,7 @@ The following arguments are supported: * `sub_domain` - (Required, String) Custom domain name to be bound. * `certificate_id` - (Optional, String) Unique certificate ID of the custom domain name to be bound. You can choose to upload for the `protocol` attribute value `https` or `http&https`. * `is_default_mapping` - (Optional, Bool) Whether the default path mapping is used. The default value is `true`. When it is `false`, it means custom path mapping. In this case, the `path_mappings` attribute is required. +* `is_forced_https` - (Optional, Bool) Whether to force HTTP requests to jump to HTTPS, default to false. When the parameter is true, the API gateway will redirect all HTTP protocol requests using the custom domain name to the HTTPS protocol for forwarding. * `path_mappings` - (Optional, Set: [`String`]) Custom domain name path mapping. The data format is: `path#environment`. Optional values for the environment are `test`, `prepub`, and `release`. ## Attributes Reference diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 07f104f377..5811bef701 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -30,6 +30,12 @@