From e15e9e5755079b5af87286a9fc56b3e2ca303a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Thu, 21 Dec 2023 17:21:02 +0100 Subject: [PATCH 1/5] Generated resource --- .../resource_datadog_openapi_api.go | 210 ++++++++++++++++++ .../resource_datadog_openapi_api_test.go | 105 +++++++++ .../resources/datadog_openapi_api/import.sh | 1 + .../resources/datadog_openapi_api/resource.tf | 6 + 4 files changed, 322 insertions(+) create mode 100644 datadog/fwprovider/resource_datadog_openapi_api.go create mode 100644 datadog/tests/resource_datadog_openapi_api_test.go create mode 100644 examples/resources/datadog_openapi_api/import.sh create mode 100644 examples/resources/datadog_openapi_api/resource.tf diff --git a/datadog/fwprovider/resource_datadog_openapi_api.go b/datadog/fwprovider/resource_datadog_openapi_api.go new file mode 100644 index 0000000000..0327c2b686 --- /dev/null +++ b/datadog/fwprovider/resource_datadog_openapi_api.go @@ -0,0 +1,210 @@ + +package fwprovider + +import ( + "context" + + "github.com/DataDog/datadog-api-client-go/v2/api/datadog" + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + "github.com/hashicorp/terraform-plugin-framework/diag" + frameworkPath "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" +) + +var ( + _ resource.ResourceWithConfigure = &openapiApiResource{} + _ resource.ResourceWithImportState = &openapiApiResource{} +) + + + +type openapiApiResource struct { + Api *datadogV2.APIManagementApi + Auth context.Context +} + +type openapiApiModel struct { + ID types.String `tfsdk:"id"` +} + + + + + +func NewOpenapiApiResource() resource.Resource { + return &openapiApiResource{} +} + +func (r *openapiApiResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { + providerData, _ := request.ProviderData.(*FrameworkProvider) + r.Api = providerData.DatadogApiInstances.GetAPIManagementApiV2() + r.Auth = providerData.Auth +} + +func (r *openapiApiResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "openapi_api" +} + + + +func (r *openapiApiResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ + Description: "Provides a Datadog OpenapiApi resource. This can be used to create and manage Datadog openapi_api.", + Attributes: map[string]schema.Attribute{ + "id": utils.ResourceIDAttribute(), + }, + } +} + +func (r *openapiApiResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, frameworkPath.Root("id"), request, response) +} + +func (r *openapiApiResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var state openapiApiModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + resp, httpResp, err := r.Api.GetOpenAPI(r.Auth, id,) + if err != nil { + if httpResp != nil && httpResp.StatusCode == 404 { + response.State.RemoveResource(ctx) + return + } + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + + r.updateState(ctx, &state, &resp) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + + + +func (r *openapiApiResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var state openapiApiModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + specFile := state.SpecFile.ValueString() + + body, diags := r.buildOpenapiApiRequestBody(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + resp, _, err := r.Api.CreateOpenAPI(r.Auth, specFile, *body, ) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + r.updateState(ctx, &state, &resp) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + + + +func (r *openapiApiResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var state openapiApiModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + + specFile := state.SpecFile.ValueString() + + body, diags := r.buildOpenapiApiUpdateRequestBody(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + + resp, _, err := r.Api.UpdateOpenAPI(r.Auth, id, specFile, *body, ) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + r.updateState(ctx, &state, &resp) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + + + +func (r *openapiApiResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var state openapiApiModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + + httpResp, err := r.Api.DeleteOpenAPI(r.Auth, id,) + if err != nil { + if httpResp != nil && httpResp.StatusCode == 404 { + return + } + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting openapi_api")) + return + } +} + + +func (r *openapiApiResource) updateState(ctx context.Context, state *openapiApiModel, resp *datadogV2.OpenAPIfile) { + state.ID = types.StringValue(resp.GetId()) + + + if specFile, ok := resp.GetSpecFileOk(); ok { + state.SpecFile = types.StringValue(*specFile) + } +} + + + + +func (r *openapiApiResource) buildOpenapiApiRequestBody(ctx context.Context, state *openapiApiModel) (*datadogV2.string, diag.Diagnostics) { + diags := diag.Diagnostics{} + + + return req, diags +} + + + +func (r *openapiApiResource) buildOpenapiApiUpdateRequestBody(ctx context.Context, state *openapiApiModel) (*datadogV2.string, diag.Diagnostics) { + diags := diag.Diagnostics{} + + + return req, diags +} diff --git a/datadog/tests/resource_datadog_openapi_api_test.go b/datadog/tests/resource_datadog_openapi_api_test.go new file mode 100644 index 0000000000..df1472ef37 --- /dev/null +++ b/datadog/tests/resource_datadog_openapi_api_test.go @@ -0,0 +1,105 @@ + +package test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" + "github.com/terraform-providers/terraform-provider-datadog/datadog" + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" +) + +func TestAccOpenapiApiBasic(t *testing.T) { + t.Parallel() + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + uniq := uniqueEntityName(ctx, t) + + resource.Test(t, resource.TestCase{ + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckDatadogOpenapiApiDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + Config: testAccCheckDatadogOpenapiApi(uniq), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatadogOpenapiApiExists(providers.frameworkProvider), + + + ), + }, + }, + }) +} + +func testAccCheckDatadogOpenapiApi(uniq string) string { +// Update me to make use of the unique value + return fmt.Sprintf(`resource "datadog_openapi_api" "foo" { + spec_file = "UPDATE ME" + body = "UPDATE ME" +}`, uniq) +} + +func testAccCheckDatadogOpenapiApiDestroy(accProvider *fwprovider.FrameworkProvider) func(*terraform.State) error { + return func(s *terraform.State) error { + apiInstances := accProvider.DatadogApiInstances + auth := accProvider.Auth + + if err := OpenapiApiDestroyHelper(auth, s, apiInstances); err != nil { + return err + } + return nil + } +} + +func OpenapiApiDestroyHelper(auth context.Context, s *terraform.State, apiInstances *utils.ApiInstances) error { + err := utils.Retry(2, 10, func() error { + for _, r := range s.RootModule().Resources { + if r.Type != "resource_datadog_openapi_api" { + continue + } + id := r.Primary.ID + + _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id,) + if err != nil { + if httpResp != nil && httpResp.StatusCode == 404 { + return nil + } + return &utils.RetryableError{Prob: fmt.Sprintf("received an error retrieving OpenapiApi %s", err)} + } + return &utils.RetryableError{Prob: "OpenapiApi still exists"} + } + return nil + }) + return err +} + +func testAccCheckDatadogOpenapiApiExists(accProvider *fwprovider.FrameworkProvider) resource.TestCheckFunc { + return func(s *terraform.State) error { + apiInstances := accProvider.DatadogApiInstances + auth := accProvider.Auth + + if err := openapiApiExistsHelper(auth, s, apiInstances); err != nil { + return err + } + return nil + } +} + +func openapiApiExistsHelper(auth context.Context, s *terraform.State, apiInstances *utils.ApiInstances) error { + for _, r := range s.RootModule().Resources { + if r.Type != "resource_datadog_openapi_api" { + continue + } + id := r.Primary.ID + + _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id,) + if err != nil { + return utils.TranslateClientError(err, httpResp, "error retrieving OpenapiApi") + } + } + return nil +} \ No newline at end of file diff --git a/examples/resources/datadog_openapi_api/import.sh b/examples/resources/datadog_openapi_api/import.sh new file mode 100644 index 0000000000..805e598590 --- /dev/null +++ b/examples/resources/datadog_openapi_api/import.sh @@ -0,0 +1 @@ +terraform import datadog_openapi_api.new_list "90646597-5fdb-4a17-a240-647003f8c028" \ No newline at end of file diff --git a/examples/resources/datadog_openapi_api/resource.tf b/examples/resources/datadog_openapi_api/resource.tf new file mode 100644 index 0000000000..7a0eb432c5 --- /dev/null +++ b/examples/resources/datadog_openapi_api/resource.tf @@ -0,0 +1,6 @@ +# Create new openapi_api resource + +resource "datadog_openapi_api" "foo" { + spec_file = "UPDATE ME" + body = "UPDATE ME" +} \ No newline at end of file From 77ab73e05f2b9e0cd3b7dc2909106659c7274028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Fri, 22 Dec 2023 16:51:08 +0100 Subject: [PATCH 2/5] Working resource --- datadog/fwprovider/framework_provider.go | 1 + .../resource_datadog_openapi_api.go | 203 +++++++----------- .../internal/utils/api_instances_helper.go | 9 + .../resource_datadog_openapi_api_test.go | 31 ++- .../resources/datadog_openapi_api/resource.tf | 4 +- go.mod | 2 + 6 files changed, 110 insertions(+), 140 deletions(-) diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index ffb544a175..dbc1cc83b0 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -32,6 +32,7 @@ var ( ) var Resources = []func() resource.Resource{ + NewOpenapiApiResource, NewAPIKeyResource, NewApplicationKeyResource, NewApmRetentionFilterResource, diff --git a/datadog/fwprovider/resource_datadog_openapi_api.go b/datadog/fwprovider/resource_datadog_openapi_api.go index 0327c2b686..671ca5d27f 100644 --- a/datadog/fwprovider/resource_datadog_openapi_api.go +++ b/datadog/fwprovider/resource_datadog_openapi_api.go @@ -1,17 +1,17 @@ - package fwprovider import ( "context" + "io" + "strings" - "github.com/DataDog/datadog-api-client-go/v2/api/datadog" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" - "github.com/hashicorp/terraform-plugin-framework/diag" frameworkPath "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/google/uuid" "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) @@ -20,8 +20,6 @@ var ( _ resource.ResourceWithImportState = &openapiApiResource{} ) - - type openapiApiResource struct { Api *datadogV2.APIManagementApi Auth context.Context @@ -29,12 +27,9 @@ type openapiApiResource struct { type openapiApiModel struct { ID types.String `tfsdk:"id"` + Spec types.String `tfsdk:"spec"` } - - - - func NewOpenapiApiResource() resource.Resource { return &openapiApiResource{} } @@ -49,13 +44,15 @@ func (r *openapiApiResource) Metadata(_ context.Context, request resource.Metada response.TypeName = "openapi_api" } - - func (r *openapiApiResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) { response.Schema = schema.Schema{ Description: "Provides a Datadog OpenapiApi resource. This can be used to create and manage Datadog openapi_api.", Attributes: map[string]schema.Attribute{ - "id": utils.ResourceIDAttribute(), + "id": utils.ResourceIDAttribute(), + "spec": schema.StringAttribute{ + Description: "The OpenAPI spec.", + Required: true, + }, }, } } @@ -65,14 +62,15 @@ func (r *openapiApiResource) ImportState(ctx context.Context, request resource.I } func (r *openapiApiResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { - var state openapiApiModel - response.Diagnostics.Append(request.State.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } - - id := state.ID.ValueString() - resp, httpResp, err := r.Api.GetOpenAPI(r.Auth, id,) + var state openapiApiModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + id := state.ID.ValueString() + uuid, _ := uuid.Parse(id) + resp, httpResp, err := r.Api.GetOpenAPI(r.Auth, uuid) if err != nil { if httpResp != nil && httpResp.StatusCode == 404 { response.State.RemoveResource(ctx) @@ -81,96 +79,88 @@ func (r *openapiApiResource) Read(ctx context.Context, request resource.ReadRequ response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) return } - if err := utils.CheckForUnparsed(resp); err != nil { - response.Diagnostics.AddError("response contains unparsedObject", err.Error()) - return - } - - r.updateState(ctx, &state, &resp) + specData, err := io.ReadAll(resp) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error reading spec")) + return + } + state.Spec = types.StringValue(string(specData)) - // Save data into Terraform state - response.Diagnostics.Append(response.State.Set(ctx, &state)...) + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) } +func (r *openapiApiResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var state openapiApiModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + specFile := state.Spec.ValueString() -func (r *openapiApiResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { - var state openapiApiModel - response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } - - specFile := state.SpecFile.ValueString() - - body, diags := r.buildOpenapiApiRequestBody(ctx, &state) - response.Diagnostics.Append(diags...) - if response.Diagnostics.HasError() { - return - } - - resp, _, err := r.Api.CreateOpenAPI(r.Auth, specFile, *body, ) + var bodyReader io.Reader + bodyReader = strings.NewReader(specFile) + params := datadogV2.NewCreateOpenAPIOptionalParameters().WithOpenapiSpecFile(bodyReader) + resp, _, err := r.Api.CreateOpenAPI(r.Auth, *params) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) - return + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) + return } - if err := utils.CheckForUnparsed(resp); err != nil { - response.Diagnostics.AddError("response contains unparsedObject", err.Error()) - return - } - r.updateState(ctx, &state, &resp) - - // Save data into Terraform state - response.Diagnostics.Append(response.State.Set(ctx, &state)...) -} - + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + respData := resp.GetData() + state.ID = types.StringValue(respData.GetId().String()) + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} func (r *openapiApiResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { - var state openapiApiModel - response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } - + var state openapiApiModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + id := state.ID.ValueString() - - specFile := state.SpecFile.ValueString() - - body, diags := r.buildOpenapiApiUpdateRequestBody(ctx, &state) - response.Diagnostics.Append(diags...) - if response.Diagnostics.HasError() { - return - } - - - resp, _, err := r.Api.UpdateOpenAPI(r.Auth, id, specFile, *body, ) + uuid, _ := uuid.Parse(id) + + specFile := state.Spec.ValueString() + + var bodyReader io.Reader + bodyReader = strings.NewReader(specFile) + params := datadogV2.NewUpdateOpenAPIOptionalParameters().WithOpenapiSpecFile(bodyReader) + + resp, _, err := r.Api.UpdateOpenAPI(r.Auth, uuid, *params) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) - return + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi")) + return } - if err := utils.CheckForUnparsed(resp); err != nil { - response.Diagnostics.AddError("response contains unparsedObject", err.Error()) - return - } - r.updateState(ctx, &state, &resp) - - // Save data into Terraform state - response.Diagnostics.Append(response.State.Set(ctx, &state)...) -} - + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + respData := resp.GetData() + state.ID = types.StringValue(respData.GetId().String()) + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} func (r *openapiApiResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { - var state openapiApiModel - response.Diagnostics.Append(request.State.Get(ctx, &state)...) - if response.Diagnostics.HasError() { - return - } + var state openapiApiModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } - id := state.ID.ValueString() + id := state.ID.ValueString() + uuid, _ := uuid.Parse(id) - httpResp, err := r.Api.DeleteOpenAPI(r.Auth, id,) + httpResp, err := r.Api.DeleteOpenAPI(r.Auth, uuid) if err != nil { if httpResp != nil && httpResp.StatusCode == 404 { return @@ -179,32 +169,3 @@ func (r *openapiApiResource) Delete(ctx context.Context, request resource.Delete return } } - - -func (r *openapiApiResource) updateState(ctx context.Context, state *openapiApiModel, resp *datadogV2.OpenAPIfile) { - state.ID = types.StringValue(resp.GetId()) - - - if specFile, ok := resp.GetSpecFileOk(); ok { - state.SpecFile = types.StringValue(*specFile) - } -} - - - - -func (r *openapiApiResource) buildOpenapiApiRequestBody(ctx context.Context, state *openapiApiModel) (*datadogV2.string, diag.Diagnostics) { - diags := diag.Diagnostics{} - - - return req, diags -} - - - -func (r *openapiApiResource) buildOpenapiApiUpdateRequestBody(ctx context.Context, state *openapiApiModel) (*datadogV2.string, diag.Diagnostics) { - diags := diag.Diagnostics{} - - - return req, diags -} diff --git a/datadog/internal/utils/api_instances_helper.go b/datadog/internal/utils/api_instances_helper.go index b1b4bba1e9..c3f75d3707 100644 --- a/datadog/internal/utils/api_instances_helper.go +++ b/datadog/internal/utils/api_instances_helper.go @@ -44,6 +44,7 @@ type ApiInstances struct { webhooksIntegrationApiV1 *datadogV1.WebhooksIntegrationApi //V2 APIs + apiManagementAPIV2 *datadogV2.APIManagementApi apmRetentionFiltersApiV2 *datadogV2.APMRetentionFiltersApi auditApiV2 *datadogV2.AuditApi authNMappingsApiV2 *datadogV2.AuthNMappingsApi @@ -617,3 +618,11 @@ func (i *ApiInstances) GetApmRetentionFiltersApiV2() *datadogV2.APMRetentionFilt } return i.apmRetentionFiltersApiV2 } + +// GetAPIManagementApiV2 get instance of APIManagementApi +func (i *ApiInstances) GetAPIManagementApiV2() *datadogV2.APIManagementApi { + if i.apiManagementAPIV2 == nil { + i.apiManagementAPIV2 = datadogV2.NewAPIManagementApi(i.HttpClient) + } + return i.apiManagementAPIV2 +} diff --git a/datadog/tests/resource_datadog_openapi_api_test.go b/datadog/tests/resource_datadog_openapi_api_test.go index df1472ef37..187c65c0ce 100644 --- a/datadog/tests/resource_datadog_openapi_api_test.go +++ b/datadog/tests/resource_datadog_openapi_api_test.go @@ -1,4 +1,3 @@ - package test import ( @@ -9,8 +8,8 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" "github.com/terraform-providers/terraform-provider-datadog/datadog" + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) @@ -21,14 +20,12 @@ func TestAccOpenapiApiBasic(t *testing.T) { resource.Test(t, resource.TestCase{ ProtoV5ProviderFactories: accProviders, - CheckDestroy: testAccCheckDatadogOpenapiApiDestroy(providers.frameworkProvider), + CheckDestroy: testAccCheckDatadogOpenapiApiDestroy(providers.frameworkProvider), Steps: []resource.TestStep{ { Config: testAccCheckDatadogOpenapiApi(uniq), Check: resource.ComposeTestCheckFunc( testAccCheckDatadogOpenapiApiExists(providers.frameworkProvider), - - ), }, }, @@ -36,7 +33,7 @@ func TestAccOpenapiApiBasic(t *testing.T) { } func testAccCheckDatadogOpenapiApi(uniq string) string { -// Update me to make use of the unique value + // Update me to make use of the unique value return fmt.Sprintf(`resource "datadog_openapi_api" "foo" { spec_file = "UPDATE ME" body = "UPDATE ME" @@ -58,12 +55,12 @@ func testAccCheckDatadogOpenapiApiDestroy(accProvider *fwprovider.FrameworkProvi func OpenapiApiDestroyHelper(auth context.Context, s *terraform.State, apiInstances *utils.ApiInstances) error { err := utils.Retry(2, 10, func() error { for _, r := range s.RootModule().Resources { - if r.Type != "resource_datadog_openapi_api" { - continue - } - id := r.Primary.ID + if r.Type != "resource_datadog_openapi_api" { + continue + } + id := r.Primary.ID - _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id,) + _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id) if err != nil { if httpResp != nil && httpResp.StatusCode == 404 { return nil @@ -91,15 +88,15 @@ func testAccCheckDatadogOpenapiApiExists(accProvider *fwprovider.FrameworkProvid func openapiApiExistsHelper(auth context.Context, s *terraform.State, apiInstances *utils.ApiInstances) error { for _, r := range s.RootModule().Resources { - if r.Type != "resource_datadog_openapi_api" { - continue - } - id := r.Primary.ID + if r.Type != "resource_datadog_openapi_api" { + continue + } + id := r.Primary.ID - _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id,) + _, httpResp, err := apiInstances.GetAPIManagementApiV2().GetOpenAPI(auth, id) if err != nil { return utils.TranslateClientError(err, httpResp, "error retrieving OpenapiApi") } } return nil -} \ No newline at end of file +} diff --git a/examples/resources/datadog_openapi_api/resource.tf b/examples/resources/datadog_openapi_api/resource.tf index 7a0eb432c5..047251455e 100644 --- a/examples/resources/datadog_openapi_api/resource.tf +++ b/examples/resources/datadog_openapi_api/resource.tf @@ -1,6 +1,6 @@ # Create new openapi_api resource resource "datadog_openapi_api" "foo" { - spec_file = "UPDATE ME" - body = "UPDATE ME" + spec_file = "UPDATE ME" + body = "UPDATE ME" } \ No newline at end of file diff --git a/go.mod b/go.mod index be7aaf2e0e..717329e999 100644 --- a/go.mod +++ b/go.mod @@ -97,3 +97,5 @@ require ( ) go 1.20 + +replace github.com/DataDog/datadog-api-client-go/v2 => /home/therve/Projects/datadog-api-client-go From ca41a846157bfb0f3adaf505e9cc493b4095ce3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Fri, 19 Jan 2024 14:52:19 +0100 Subject: [PATCH 3/5] Add test --- datadog/fwprovider/framework_provider.go | 5 + .../cassettes/TestAccOpenapiApiBasic.freeze | 1 + .../cassettes/TestAccOpenapiApiBasic.yaml | 114 ++++++++++++++++++ datadog/tests/framework_provider_test.go | 5 + datadog/tests/provider_test.go | 1 + .../resource_datadog_openapi_api_test.go | 39 ++++-- .../resources/datadog_openapi_api/resource.tf | 7 +- go.mod | 6 +- go.sum | 8 +- 9 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 datadog/tests/cassettes/TestAccOpenapiApiBasic.freeze create mode 100644 datadog/tests/cassettes/TestAccOpenapiApiBasic.yaml diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index dbc1cc83b0..652540e8a0 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -363,6 +363,11 @@ func defaultConfigureFunc(p *FrameworkProvider, request *provider.ConfigureReque ddClientConfig.UserAgent = utils.GetUserAgentFramework(ddClientConfig.UserAgent, request.TerraformVersion) ddClientConfig.Debug = logging.IsDebugOrHigher() + ddClientConfig.SetUnstableOperationEnabled("v2.CreateOpenAPI", true) + ddClientConfig.SetUnstableOperationEnabled("v2.UpdateOpenAPI", true) + ddClientConfig.SetUnstableOperationEnabled("v2.GetOpenAPI", true) + ddClientConfig.SetUnstableOperationEnabled("v2.DeleteOpenAPI", true) + if !config.ApiUrl.IsNull() && config.ApiUrl.ValueString() != "" { parsedAPIURL, parseErr := url.Parse(config.ApiUrl.ValueString()) if parseErr != nil { diff --git a/datadog/tests/cassettes/TestAccOpenapiApiBasic.freeze b/datadog/tests/cassettes/TestAccOpenapiApiBasic.freeze new file mode 100644 index 0000000000..429335ce15 --- /dev/null +++ b/datadog/tests/cassettes/TestAccOpenapiApiBasic.freeze @@ -0,0 +1 @@ +2024-01-29T13:46:34.377795845+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccOpenapiApiBasic.yaml b/datadog/tests/cassettes/TestAccOpenapiApiBasic.yaml new file mode 100644 index 0000000000..92d9f74463 --- /dev/null +++ b/datadog/tests/cassettes/TestAccOpenapiApiBasic.yaml @@ -0,0 +1,114 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 446 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "--b83b2fd69da78bf28017af4b8c09378bf61fa1caa18e15bf1b49ab6e16f4\r\nContent-Disposition: form-data; name=\"openapi_spec_file\"; filename=\".\"\r\nContent-Type: application/octet-stream\r\n\r\ninfo:\n contact:\n name: API team\n description: My API description.\n title: tf-TestAccOpenapiApiBasic-local-1706532394\nopenapi: 3.0.0\npaths:\n /api/my-api:\n get:\n description: An endpoint\n\r\n--b83b2fd69da78bf28017af4b8c09378bf61fa1caa18e15bf1b49ab6e16f4--\r\n" + form: {} + headers: + Accept: + - application/json + Content-Type: + - multipart/form-data; boundary=b83b2fd69da78bf28017af4b8c09378bf61fa1caa18e15bf1b49ab6e16f4 + url: https://api.datadoghq.com/api/v2/apicatalog/openapi + method: POST + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 104 + uncompressed: false + body: '{"data":{"id":"3326a3a7-6d74-48b7-9a63-bd7b5656ed6f","type":"api","attributes":{"failed_endpoints":[]}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 409.277916ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/apicatalog/api/3326a3a7-6d74-48b7-9a63-bd7b5656ed6f/openapi + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 200 + uncompressed: false + body: | + info: + contact: + name: API team + description: My API description. + title: tf-TestAccOpenapiApiBasic-local-1706532394 + openapi: 3.0.0 + paths: + /api/my-api: + get: + description: An endpoint + headers: + Content-Type: + - multipart/form-data + status: 200 OK + code: 200 + duration: 113.217858ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - '*/*' + url: https://api.datadoghq.com/api/v2/apicatalog/api/3326a3a7-6d74-48b7-9a63-bd7b5656ed6f + method: DELETE + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 0 + uncompressed: false + body: "" + headers: + Content-Type: + - application/vnd.api+json + status: 204 No Content + code: 204 + duration: 117.394279ms diff --git a/datadog/tests/framework_provider_test.go b/datadog/tests/framework_provider_test.go index 2ee0c486f9..a76f03d4d0 100644 --- a/datadog/tests/framework_provider_test.go +++ b/datadog/tests/framework_provider_test.go @@ -33,6 +33,11 @@ func buildFrameworkDatadogClient(ctx context.Context, httpClient *http.Client) * //Datadog API config.HTTPClient config := common.NewConfiguration() + config.SetUnstableOperationEnabled("v2.CreateOpenAPI", true) + config.SetUnstableOperationEnabled("v2.UpdateOpenAPI", true) + config.SetUnstableOperationEnabled("v2.GetOpenAPI", true) + config.SetUnstableOperationEnabled("v2.DeleteOpenAPI", true) + if ctx.Value("http_retry_enable") == true { config.RetryConfiguration.EnableRetry = true } diff --git a/datadog/tests/provider_test.go b/datadog/tests/provider_test.go index 0aec8a9259..a5e5af0400 100644 --- a/datadog/tests/provider_test.go +++ b/datadog/tests/provider_test.go @@ -138,6 +138,7 @@ var testFiles2EndpointTags = map[string]string{ "tests/resource_datadog_dashboard_topology_map_test": "dashboards", "tests/resource_datadog_dashboard_trace_service_test": "dashboards", "tests/resource_datadog_dashboard_treemap_test": "dashboards", + "tests/resource_datadog_openapi_api_test": "apimanagement", "tests/resource_datadog_powerpack_test": "powerpacks", "tests/resource_datadog_powerpack_alert_graph_test": "powerpacks", "tests/resource_datadog_powerpack_alert_value_test": "powerpacks", diff --git a/datadog/tests/resource_datadog_openapi_api_test.go b/datadog/tests/resource_datadog_openapi_api_test.go index 187c65c0ce..0a1f459e47 100644 --- a/datadog/tests/resource_datadog_openapi_api_test.go +++ b/datadog/tests/resource_datadog_openapi_api_test.go @@ -8,11 +8,24 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" - "github.com/terraform-providers/terraform-provider-datadog/datadog" + "github.com/google/uuid" "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) +const apiContent = ` +info: + contact: + name: API team + description: My API description. + title: %s +openapi: 3.0.0 +paths: + /api/my-api: + get: + description: An endpoint +` + func TestAccOpenapiApiBasic(t *testing.T) { t.Parallel() ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) @@ -33,10 +46,20 @@ func TestAccOpenapiApiBasic(t *testing.T) { } func testAccCheckDatadogOpenapiApi(uniq string) string { - // Update me to make use of the unique value - return fmt.Sprintf(`resource "datadog_openapi_api" "foo" { - spec_file = "UPDATE ME" - body = "UPDATE ME" + return fmt.Sprintf(` +resource "datadog_openapi_api" "test-api" { + spec = < /home/therve/Projects/datadog-api-client-go diff --git a/go.sum b/go.sum index f77b988e69..04a218906d 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/datadog-api-client-go/v2 v2.21.1-0.20240125185137-c5d2119fede6 h1:KlX1O7Jfz0GE9LzEIrP6xXzimChrUAtvNQNBD7BSdYg= -github.com/DataDog/datadog-api-client-go/v2 v2.21.1-0.20240125185137-c5d2119fede6/go.mod h1:lHlfhsNQ2qZclvpVQTfrGowUDIdIzAao38A05f9EQpc= +github.com/DataDog/datadog-api-client-go/v2 v2.21.1-0.20240126170449-0d471e4f5276 h1:f9Q2bB8fbgbjgw2lO2pbf/L6N1/jza6gKtkNw9WOxL4= +github.com/DataDog/datadog-api-client-go/v2 v2.21.1-0.20240126170449-0d471e4f5276/go.mod h1:QKOu6vscsh87fMY1lHfLEmNSunyXImj8BUaUWJXOehc= github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -97,8 +97,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= From 578452f502e40df18167575a56f69b33a840cc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Mon, 29 Jan 2024 14:03:15 +0100 Subject: [PATCH 4/5] Docs --- docs/resources/openapi_api.md | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/resources/openapi_api.md diff --git a/docs/resources/openapi_api.md b/docs/resources/openapi_api.md new file mode 100644 index 0000000000..ad280f9105 --- /dev/null +++ b/docs/resources/openapi_api.md @@ -0,0 +1,40 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_openapi_api Resource - terraform-provider-datadog" +subcategory: "" +description: |- + Provides a Datadog OpenapiApi resource. This can be used to create and manage Datadog openapi_api. +--- + +# datadog_openapi_api (Resource) + +Provides a Datadog OpenapiApi resource. This can be used to create and manage Datadog openapi_api. + +## Example Usage + +```terraform +# Create new openapi_api resource + +resource "datadog_openapi_api" "my-api" { + spec = file("./path/my-api.yaml") +} +``` + + +## Schema + +### Required + +- `spec` (String) The OpenAPI spec. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import datadog_openapi_api.new_list "90646597-5fdb-4a17-a240-647003f8c028" +``` From 1fb78b2a481d02750863f09643e7096658831c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Tue, 30 Jan 2024 07:42:31 +0100 Subject: [PATCH 5/5] Remove unused --- datadog/tests/resource_datadog_openapi_api_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/datadog/tests/resource_datadog_openapi_api_test.go b/datadog/tests/resource_datadog_openapi_api_test.go index 0a1f459e47..c2a1e7d395 100644 --- a/datadog/tests/resource_datadog_openapi_api_test.go +++ b/datadog/tests/resource_datadog_openapi_api_test.go @@ -13,19 +13,6 @@ import ( "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) -const apiContent = ` -info: - contact: - name: API team - description: My API description. - title: %s -openapi: 3.0.0 -paths: - /api/my-api: - get: - description: An endpoint -` - func TestAccOpenapiApiBasic(t *testing.T) { t.Parallel() ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t)