Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[datadog_openapi_api] Add support for API management resource #2262

Merged
merged 5 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions datadog/fwprovider/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (
)

var Resources = []func() resource.Resource{
NewOpenapiApiResource,
NewAPIKeyResource,
NewApplicationKeyResource,
NewApmRetentionFilterResource,
Expand Down Expand Up @@ -362,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 {
Expand Down
171 changes: 171 additions & 0 deletions datadog/fwprovider/resource_datadog_openapi_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package fwprovider

import (
"context"
"io"
"strings"

"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
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"
)

var (
_ resource.ResourceWithConfigure = &openapiApiResource{}
_ resource.ResourceWithImportState = &openapiApiResource{}
)

type openapiApiResource struct {
Api *datadogV2.APIManagementApi
Auth context.Context
}

type openapiApiModel struct {
ID types.String `tfsdk:"id"`
Spec types.String `tfsdk:"spec"`
}

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(),
"spec": schema.StringAttribute{
Description: "The OpenAPI spec.",
Required: true,
},
},
}
}

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()
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)
return
}
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving OpenapiApi"))
return
}
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)...)
}

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()

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
}
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
}

id := state.ID.ValueString()
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
}
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
}

id := state.ID.ValueString()
uuid, _ := uuid.Parse(id)

httpResp, err := r.Api.DeleteOpenAPI(r.Auth, uuid)
if err != nil {
if httpResp != nil && httpResp.StatusCode == 404 {
return
}
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting openapi_api"))
return
}
}
9 changes: 9 additions & 0 deletions datadog/internal/utils/api_instances_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type ApiInstances struct {
webhooksIntegrationApiV1 *datadogV1.WebhooksIntegrationApi

//V2 APIs
apiManagementAPIV2 *datadogV2.APIManagementApi
apmRetentionFiltersApiV2 *datadogV2.APMRetentionFiltersApi
auditApiV2 *datadogV2.AuditApi
authNMappingsApiV2 *datadogV2.AuthNMappingsApi
Expand Down Expand Up @@ -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
}
1 change: 1 addition & 0 deletions datadog/tests/cassettes/TestAccOpenapiApiBasic.freeze
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2024-01-29T13:46:34.377795845+01:00
114 changes: 114 additions & 0 deletions datadog/tests/cassettes/TestAccOpenapiApiBasic.yaml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions datadog/tests/framework_provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions datadog/tests/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading
Loading