diff --git a/Scripts/TerraformProvider/Core/OpenAPIParser.ts b/Scripts/TerraformProvider/Core/OpenAPIParser.ts index e2bd3a8fa3..7bfb1d2ff0 100644 --- a/Scripts/TerraformProvider/Core/OpenAPIParser.ts +++ b/Scripts/TerraformProvider/Core/OpenAPIParser.ts @@ -570,6 +570,7 @@ export class OpenAPIParser { const prop: any = propSchema as any; let propType: string = prop.type || "string"; + let propFormat: string | undefined = prop.format; // Capture the format field let description: string = prop.description || ""; let example: any = prop.example; let defaultValue: any = prop.default; @@ -579,6 +580,7 @@ export class OpenAPIParser { const resolvedProp: any = this.resolveSchemaRef(prop.$ref); if (resolvedProp) { propType = resolvedProp.type || "string"; + propFormat = resolvedProp.format || propFormat; // Also capture format from resolved refs description = resolvedProp.description || description; example = resolvedProp.example || example; defaultValue = resolvedProp.default || defaultValue; @@ -661,6 +663,7 @@ export class OpenAPIParser { example: example, // Extract example from OpenAPI schema default: defaultValue, // Extract default value from OpenAPI schema isComplexObject: propType === "object", // Flag to indicate this string field is actually a complex object + ...(propFormat && { format: propFormat }), // Only include format if it exists }; } } diff --git a/Scripts/TerraformProvider/Core/ResourceGenerator.ts b/Scripts/TerraformProvider/Core/ResourceGenerator.ts index 4e7220eb88..b89750151c 100644 --- a/Scripts/TerraformProvider/Core/ResourceGenerator.ts +++ b/Scripts/TerraformProvider/Core/ResourceGenerator.ts @@ -592,6 +592,8 @@ func (r *${resourceTypeName}Resource) Create(ctx context.Context, req resource.C return } +${this.generateOriginalValueStorage(resource)} + // Create API request body ${resourceVarName}Request := map[string]interface{}{ "data": map[string]interface{}{ @@ -614,7 +616,7 @@ ${this.generateRequestBody(resource)} } // Update the model with response data -${this.generateResponseMapping(resource, resourceVarName + "Response")} +${this.generateResponseMapping(resource, resourceVarName + "Response", true)} // Write logs using the tflog package tflog.Trace(ctx, "created a resource") @@ -693,7 +695,7 @@ ${this.generateSelectParameter(resource)} } // Update the model with response data -${this.generateResponseMapping(resource, resourceVarName + "Response")} +${this.generateResponseMapping(resource, resourceVarName + "Response", false)} // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -829,7 +831,7 @@ ${this.generateSelectParameter(resource)} } // Update the model with response data from the Read operation -${this.generateResponseMapping(resource, "readResponse")} +${this.generateResponseMapping(resource, "readResponse", false)} // Save updated data into Terraform state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) @@ -1122,6 +1124,7 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D private generateResponseMapping( resource: TerraformResource, responseVar: string, + isCreateMethod: boolean = false, ): string { const mappings: string[] = []; @@ -1168,6 +1171,9 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D `dataMap["${apiFieldName}"]`, attr.default !== undefined && attr.default !== null, // hasDefault attr.isComplexObject || false, // isComplexObject + attr.format, // format + isCreateMethod, // isCreateMethod + sanitizedName, // fieldName for original value preservation ); mappings.push(` ${setter}`); } @@ -1189,10 +1195,35 @@ func (r *${resourceTypeName}Resource) Delete(ctx context.Context, req resource.D responseValue: string, hasDefault: boolean = false, isComplexObject: boolean = false, + format?: string, + isCreateMethod: boolean = false, + originalFieldName?: string, ): string { + switch (terraformType) { case "string": - if (isComplexObject) { + // Handle binary format fields (like base64 file content) specially + if (format === "binary") { + // For binary fields, treat the response as a simple string without complex object processing + if (isCreateMethod && originalFieldName) { + // In Create method, preserve original value if API doesn't return the file content + return `if val, ok := ${responseValue}.(string); ok { + ${fieldName} = types.StringValue(val) + } else { + // Preserve original value from the request since API doesn't return file content + ${fieldName} = types.StringValue(original${StringUtils.toPascalCase(originalFieldName)}Value) + }`; + } else { + // In Read/Update methods, preserve existing value if not present in API response + // This prevents drift detection when API doesn't return binary content + return `if val, ok := ${responseValue}.(string); ok { + ${fieldName} = types.StringValue(val) + } else { + // Keep existing value to prevent drift - API doesn't return binary content + // ${fieldName} value is already set from the existing state + }`; + } + } else if (isComplexObject) { // For complex object strings, convert API object response to JSON string return `if val, ok := ${responseValue}.(map[string]interface{}); ok { if jsonBytes, err := json.Marshal(val); err == nil { @@ -1384,4 +1415,21 @@ ${resourceFunctions} resourceListContent, ); } + + private generateOriginalValueStorage(resource: TerraformResource): string { + const storage: string[] = []; + + // Find binary format fields and store their original values + for (const [name, attr] of Object.entries(resource.schema)) { + if (attr.format === "binary") { + const sanitizedName: string = this.sanitizeAttributeName(name); + const fieldName: string = StringUtils.toPascalCase(sanitizedName); + storage.push(` // Store the original ${sanitizedName} value since API won't return it`); + storage.push(` original${fieldName}Value := data.${fieldName}.ValueString()`); + storage.push(``); + } + } + + return storage.join("\n"); + } } diff --git a/Scripts/TerraformProvider/Core/Types.ts b/Scripts/TerraformProvider/Core/Types.ts index 803635fd83..37e768780a 100644 --- a/Scripts/TerraformProvider/Core/Types.ts +++ b/Scripts/TerraformProvider/Core/Types.ts @@ -114,6 +114,7 @@ export interface TerraformAttribute { apiFieldName?: string; // Original OpenAPI field name for API requests example?: any; // Example value from OpenAPI spec isComplexObject?: boolean; // Flag to indicate this string field is actually a complex object + format?: string; // OpenAPI format information (e.g., "binary", "date-time", etc.) } export interface GoType {