Skip to content

Commit 65c2402

Browse files
committed
Remove OneOf manual parsing workaround for file_metadata
The workaround is no longer needed because we fixed the root cause in the OpenAPI spec by making access_details required in CloudStorage schema. This allows the Go client OneOf unmarshaler to properly distinguish between CloudStorage and LocalFile types. The spec fix makes access_details required for CloudStorage, which provides a clear discriminator: - CloudStorage requires access_details (AWS/GCP/Azure config) - LocalFile has no required fields This PR removes the temporary manual parsing logic and relies on the properly generated Go client code once the spec changes are merged and the client is regenerated.
1 parent ffab237 commit 65c2402

25 files changed

+286
-140
lines changed

.generator/src/generator/openapi.py

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import re
2-
31
from .utils import (
42
GET_OPERATION,
53
CREATE_OPERATION,
@@ -93,29 +91,9 @@ def get_data_sources(spec: dict, config: dict) -> dict:
9391

9492

9593
def get_terraform_primary_id(operations, path=UPDATE_OPERATION):
96-
"""
97-
Extract primary ID from path parameters.
98-
99-
Handles path parameters anywhere in the URL, not just at the end.
100-
For example: /api/v2/tables/{id}/rows extracts 'id'
101-
"""
102-
operation_data = operations[path]
103-
path_str = operation_data["path"]
104-
params = parameters(operation_data["schema"])
105-
106-
# Find all path parameters (anything in curly braces like {id})
107-
path_params = re.findall(r'\{([^}]+)\}', path_str)
108-
109-
if not path_params:
110-
raise ValueError(f"No path parameters found in path: {path_str}")
111-
112-
# Use the first path parameter as primary ID
113-
primary_id = path_params[0]
114-
115-
if primary_id not in params:
116-
raise ValueError(f"Path parameter '{primary_id}' not found in operation parameters")
117-
118-
primary_id_param = params.pop(primary_id)
94+
update_params = parameters(operations[path]["schema"])
95+
primary_id = operations[path]["path"].split("/")[-1][1:-1]
96+
primary_id_param = update_params.pop(primary_id)
11997

12098
return {"schema": parameter_schema(primary_id_param), "name": primary_id}
12199

datadog/fwprovider/data_source_datadog_reference_table.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,32 +27,32 @@ type datadogReferenceTableDataSourceModel struct {
2727
TableName types.String `tfsdk:"table_name"`
2828

2929
// Computed values
30-
CreatedBy types.String `tfsdk:"created_by"`
31-
Description types.String `tfsdk:"description"`
32-
LastUpdatedBy types.String `tfsdk:"last_updated_by"`
33-
RowCount types.Int64 `tfsdk:"row_count"`
34-
Source types.String `tfsdk:"source"`
35-
Status types.String `tfsdk:"status"`
36-
UpdatedAt types.String `tfsdk:"updated_at"`
37-
Tags types.List `tfsdk:"tags"`
38-
FileMetadata *tableResultV2DataAttributesFileMetadataModel `tfsdk:"file_metadata"`
39-
Schema *schemaModel `tfsdk:"schema"`
30+
CreatedBy types.String `tfsdk:"created_by"`
31+
Description types.String `tfsdk:"description"`
32+
LastUpdatedBy types.String `tfsdk:"last_updated_by"`
33+
RowCount types.Int64 `tfsdk:"row_count"`
34+
Source types.String `tfsdk:"source"`
35+
Status types.String `tfsdk:"status"`
36+
UpdatedAt types.String `tfsdk:"updated_at"`
37+
Tags types.List `tfsdk:"tags"`
38+
FileMetadata *referenceTableDataAttributesFileMetadataModel `tfsdk:"file_metadata"`
39+
Schema *schemaModel `tfsdk:"schema"`
4040
}
4141

42-
type tableResultV2DataAttributesFileMetadataModel struct {
43-
CloudStorage *tableResultV2DataAttributesFileMetadataCloudStorageModel `tfsdk:"cloud_storage"`
44-
LocalFile *tableResultV2DataAttributesFileMetadataLocalFileModel `tfsdk:"local_file"`
42+
type referenceTableDataAttributesFileMetadataModel struct {
43+
CloudStorage *referenceTableDataAttributesFileMetadataCloudStorageModel `tfsdk:"cloud_storage"`
44+
LocalFile *referenceTableDataAttributesFileMetadataLocalFileModel `tfsdk:"local_file"`
4545
}
4646

47-
type tableResultV2DataAttributesFileMetadataCloudStorageModel struct {
47+
type referenceTableDataAttributesFileMetadataCloudStorageModel struct {
4848
ErrorMessage types.String `tfsdk:"error_message"`
4949
ErrorRowCount types.Int64 `tfsdk:"error_row_count"`
5050
ErrorType types.String `tfsdk:"error_type"`
5151
SyncEnabled types.Bool `tfsdk:"sync_enabled"`
5252
AccessDetails *accessDetailsModel `tfsdk:"access_details"`
5353
}
5454

55-
type tableResultV2DataAttributesFileMetadataLocalFileModel struct {
55+
type referenceTableDataAttributesFileMetadataLocalFileModel struct {
5656
ErrorMessage types.String `tfsdk:"error_message"`
5757
ErrorRowCount types.Int64 `tfsdk:"error_row_count"`
5858
UploadId types.String `tfsdk:"upload_id"`
@@ -386,11 +386,11 @@ func (d *datadogReferenceTableDataSource) updateState(ctx context.Context, state
386386

387387
// Handle FileMetadata (OneOf union type)
388388
if fileMetadata, ok := attributes.GetFileMetadataOk(); ok {
389-
fileMetadataTf := &tableResultV2DataAttributesFileMetadataModel{}
389+
fileMetadataTf := &referenceTableDataAttributesFileMetadataModel{}
390390

391391
// Check if it's CloudStorage type
392392
if cloudStorage := fileMetadata.TableResultV2DataAttributesFileMetadataCloudStorage; cloudStorage != nil {
393-
cloudStorageTf := &tableResultV2DataAttributesFileMetadataCloudStorageModel{}
393+
cloudStorageTf := &referenceTableDataAttributesFileMetadataCloudStorageModel{}
394394

395395
if syncEnabled, ok := cloudStorage.GetSyncEnabledOk(); ok {
396396
cloudStorageTf.SyncEnabled = types.BoolValue(*syncEnabled)
@@ -474,7 +474,7 @@ func (d *datadogReferenceTableDataSource) updateState(ctx context.Context, state
474474

475475
// Check if it's LocalFile type
476476
if localFile := fileMetadata.TableResultV2DataAttributesFileMetadataLocalFile; localFile != nil {
477-
localFileTf := &tableResultV2DataAttributesFileMetadataLocalFileModel{}
477+
localFileTf := &referenceTableDataAttributesFileMetadataLocalFileModel{}
478478

479479
if uploadId, ok := localFile.GetUploadIdOk(); ok {
480480
localFileTf.UploadId = types.StringValue(*uploadId)

datadog/fwprovider/models_reference_table.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
// Shared model definitions for reference table resource and data sources
88

99
type schemaModel struct {
10-
PrimaryKeys types.List `tfsdk:"primary_keys"`
10+
PrimaryKeys types.List `tfsdk:"primary_keys"`
1111
Fields []*fieldsModel `tfsdk:"fields"`
1212
}
1313

@@ -29,17 +29,16 @@ type awsDetailModel struct {
2929
}
3030

3131
type azureDetailModel struct {
32-
AzureTenantId types.String `tfsdk:"azure_tenant_id"`
33-
AzureClientId types.String `tfsdk:"azure_client_id"`
32+
AzureTenantId types.String `tfsdk:"azure_tenant_id"`
33+
AzureClientId types.String `tfsdk:"azure_client_id"`
3434
AzureStorageAccountName types.String `tfsdk:"azure_storage_account_name"`
35-
AzureContainerName types.String `tfsdk:"azure_container_name"`
36-
FilePath types.String `tfsdk:"file_path"`
35+
AzureContainerName types.String `tfsdk:"azure_container_name"`
36+
FilePath types.String `tfsdk:"file_path"`
3737
}
3838

3939
type gcpDetailModel struct {
40-
GcpProjectId types.String `tfsdk:"gcp_project_id"`
41-
GcpBucketName types.String `tfsdk:"gcp_bucket_name"`
42-
FilePath types.String `tfsdk:"file_path"`
43-
GcpServiceAccountEmail types.String `tfsdk:"gcp_service_account_email"`
40+
GcpProjectId types.String `tfsdk:"gcp_project_id"`
41+
GcpBucketName types.String `tfsdk:"gcp_bucket_name"`
42+
FilePath types.String `tfsdk:"file_path"`
43+
GcpServiceAccountEmail types.String `tfsdk:"gcp_service_account_email"`
4444
}
45-

datadog/fwprovider/resource_datadog_reference_table.go

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -134,64 +134,64 @@ func (r *referenceTableResource) Schema(_ context.Context, _ resource.SchemaRequ
134134
Description: "Cloud storage access configuration. Exactly one of aws_detail, gcp_detail, or azure_detail must be specified.",
135135
Blocks: map[string]schema.Block{
136136
"aws_detail": schema.SingleNestedBlock{
137-
Description: "AWS S3 access configuration.",
137+
Description: "AWS S3 access configuration. Required when source is S3.",
138138
Attributes: map[string]schema.Attribute{
139139
"aws_account_id": schema.StringAttribute{
140-
Required: true,
140+
Optional: true,
141141
Description: "The ID of the AWS account.",
142142
},
143143
"aws_bucket_name": schema.StringAttribute{
144-
Required: true,
144+
Optional: true,
145145
Description: "The name of the Amazon S3 bucket.",
146146
},
147147
"file_path": schema.StringAttribute{
148-
Required: true,
148+
Optional: true,
149149
Description: "The relative file path from the S3 bucket root to the CSV file.",
150150
},
151151
},
152152
},
153153
"gcp_detail": schema.SingleNestedBlock{
154-
Description: "Google Cloud Storage access configuration.",
154+
Description: "Google Cloud Storage access configuration. Required when source is GCS.",
155155
Attributes: map[string]schema.Attribute{
156156
"gcp_project_id": schema.StringAttribute{
157-
Required: true,
157+
Optional: true,
158158
Description: "The ID of the GCP project.",
159159
},
160160
"gcp_bucket_name": schema.StringAttribute{
161-
Required: true,
161+
Optional: true,
162162
Description: "The name of the GCP bucket.",
163163
},
164164
"file_path": schema.StringAttribute{
165-
Required: true,
165+
Optional: true,
166166
Description: "The relative file path from the GCS bucket root to the CSV file.",
167167
},
168168
"gcp_service_account_email": schema.StringAttribute{
169-
Required: true,
169+
Optional: true,
170170
Description: "The email of the GCP service account used to access the bucket.",
171171
},
172172
},
173173
},
174174
"azure_detail": schema.SingleNestedBlock{
175-
Description: "Azure Blob Storage access configuration.",
175+
Description: "Azure Blob Storage access configuration. Required when source is AZURE.",
176176
Attributes: map[string]schema.Attribute{
177177
"azure_tenant_id": schema.StringAttribute{
178-
Required: true,
178+
Optional: true,
179179
Description: "The ID of the Azure tenant.",
180180
},
181181
"azure_client_id": schema.StringAttribute{
182-
Required: true,
182+
Optional: true,
183183
Description: "The Azure client ID (application ID).",
184184
},
185185
"azure_storage_account_name": schema.StringAttribute{
186-
Required: true,
186+
Optional: true,
187187
Description: "The name of the Azure storage account.",
188188
},
189189
"azure_container_name": schema.StringAttribute{
190-
Required: true,
190+
Optional: true,
191191
Description: "The name of the Azure container.",
192192
},
193193
"file_path": schema.StringAttribute{
194-
Required: true,
194+
Optional: true,
195195
Description: "The relative file path from the Azure container root to the CSV file.",
196196
},
197197
},
@@ -279,15 +279,64 @@ func (r *referenceTableResource) Create(ctx context.Context, request resource.Cr
279279
return
280280
}
281281

282-
resp, _, err := r.Api.CreateReferenceTable(r.Auth, *body)
282+
resp, httpResp, err := r.Api.CreateReferenceTable(r.Auth, *body)
283283
if err != nil {
284-
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ReferenceTable"))
284+
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error creating ReferenceTable"))
285285
return
286286
}
287287
if err := utils.CheckForUnparsed(resp); err != nil {
288288
response.Diagnostics.AddError("response contains unparsedObject", err.Error())
289289
return
290290
}
291+
292+
// If the create response doesn't include data, fetch it with a list+filter request
293+
if resp.Data == nil {
294+
if httpResp != nil && httpResp.StatusCode == 201 {
295+
// Table was created successfully, but response was empty - list tables and find by exact name
296+
tableName := state.TableName.ValueString()
297+
listResp, _, listErr := r.Api.ListTables(r.Auth)
298+
if listErr != nil {
299+
response.Diagnostics.Append(utils.FrameworkErrorDiag(listErr, "table created but error listing tables"))
300+
return
301+
}
302+
303+
// Find the table by exact name match
304+
var foundTable *datadogV2.TableResultV2Data
305+
if listResp.Data != nil {
306+
for _, table := range listResp.Data {
307+
if attrs, ok := table.GetAttributesOk(); ok {
308+
if name, nameOk := attrs.GetTableNameOk(); nameOk && *name == tableName {
309+
tableCopy := table
310+
foundTable = &tableCopy
311+
break
312+
}
313+
}
314+
}
315+
}
316+
317+
if foundTable == nil {
318+
response.Diagnostics.AddError("API Error", fmt.Sprintf("Table %s was created but not found in list", tableName))
319+
return
320+
}
321+
322+
// Get the full table details by ID
323+
tableID := foundTable.GetId()
324+
getResp, _, getErr := r.Api.GetTable(r.Auth, tableID)
325+
if getErr != nil {
326+
response.Diagnostics.Append(utils.FrameworkErrorDiag(getErr, fmt.Sprintf("table created but error fetching details for ID %s", tableID)))
327+
return
328+
}
329+
resp = getResp
330+
} else {
331+
statusCode := 0
332+
if httpResp != nil {
333+
statusCode = httpResp.StatusCode
334+
}
335+
response.Diagnostics.AddError("API Error", fmt.Sprintf("CreateReferenceTable returned an empty response (HTTP %d).", statusCode))
336+
return
337+
}
338+
}
339+
291340
r.updateState(ctx, &state, &resp)
292341

293342
// Save data into Terraform state
@@ -339,6 +388,11 @@ func (r *referenceTableResource) Delete(ctx context.Context, request resource.De
339388
}
340389

341390
func (r *referenceTableResource) updateState(ctx context.Context, state *referenceTableModel, resp *datadogV2.TableResultV2) {
391+
// Check if Data is present
392+
if resp == nil || resp.Data == nil {
393+
return
394+
}
395+
342396
attributes := resp.Data.GetAttributes()
343397

344398
state.ID = types.StringValue(*resp.GetData().Id)
@@ -568,7 +622,7 @@ func (r *referenceTableResource) buildReferenceTableRequestBody(ctx context.Cont
568622
}
569623

570624
req := datadogV2.NewCreateTableRequestWithDefaults()
571-
req.Data = datadogV2.NewCreateTableRequestDataWithDefaults()
625+
req.Data = datadogV2.NewCreateTableRequestData(datadogV2.CREATETABLEREQUESTDATATYPE_REFERENCE_TABLE)
572626
req.Data.SetAttributes(*attributes)
573627

574628
return req, diags
@@ -664,7 +718,7 @@ func (r *referenceTableResource) buildReferenceTableUpdateRequestBody(ctx contex
664718
}
665719

666720
req := datadogV2.NewPatchTableRequestWithDefaults()
667-
req.Data = datadogV2.NewPatchTableRequestDataWithDefaults()
721+
req.Data = datadogV2.NewPatchTableRequestData(datadogV2.PATCHTABLEREQUESTDATATYPE_REFERENCE_TABLE)
668722
req.Data.SetAttributes(*attributes)
669723

670724
return req, diags
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2025-11-05T18:58:39.163767-05:00
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
version: 2
3+
interactions: []
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2025-11-05T18:58:39.171125-05:00
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
version: 2
3+
interactions: []
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2025-11-05T18:58:39.139442-05:00
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
version: 2
3+
interactions: []

0 commit comments

Comments
 (0)