@@ -200,6 +200,55 @@ func convertDynamicRepSpecs(resourceb *hclwrite.Body, dSpec dynamicBlock, diskSi
200200 return nil
201201}
202202
203+ // Helper function to add attributes in order
204+ func addAttributesInOrder (targetBody * hclwrite.Body , sourceAttrs map [string ]* hclwrite.Attribute ,
205+ orderedNames []string ) {
206+ // Add ordered attributes first
207+ for _ , name := range orderedNames {
208+ if attr , exists := sourceAttrs [name ]; exists {
209+ targetBody .SetAttributeRaw (name , hcl .TokensFromExpr (hcl .GetAttrExpr (attr )))
210+ }
211+ }
212+
213+ // Add any remaining attributes alphabetically
214+ var remainingAttrs []string
215+ for name := range sourceAttrs {
216+ found := false
217+ for _ , orderedName := range orderedNames {
218+ if name == orderedName {
219+ found = true
220+ break
221+ }
222+ }
223+ if ! found {
224+ remainingAttrs = append (remainingAttrs , name )
225+ }
226+ }
227+ slices .Sort (remainingAttrs )
228+ for _ , name := range remainingAttrs {
229+ attr := sourceAttrs [name ]
230+ targetBody .SetAttributeRaw (name , hcl .TokensFromExpr (hcl .GetAttrExpr (attr )))
231+ }
232+ }
233+
234+ // Helper function to process blocks for region configs
235+ func processRegionConfigBlocks (targetBody * hclwrite.Body , blocks []* hclwrite.Block ) {
236+ for _ , block := range blocks {
237+ blockType := block .Type ()
238+ blockFile := hclwrite .NewEmptyFile ()
239+ blockBody := blockFile .Body ()
240+
241+ // For electable_specs, use specific order: instance_size, node_count
242+ var attrOrder []string
243+ if blockType == "electable_specs" {
244+ attrOrder = []string {"instance_size" , "node_count" }
245+ }
246+
247+ addAttributesInOrder (blockBody , block .Body ().Attributes (), attrOrder )
248+ targetBody .SetAttributeRaw (blockType , hcl .TokensObject (blockBody ))
249+ }
250+ }
251+
203252func convertDynamicRepSpecsWithDynamicConfig (resourceb * hclwrite.Body , dSpec , dConfig dynamicBlock ,
204253 diskSizeGB hclwrite.Tokens ) error {
205254 // Get the block name from the dynamic block
@@ -226,51 +275,20 @@ func convertDynamicRepSpecsWithDynamicConfig(resourceb *hclwrite.Body, dSpec, dC
226275 }
227276 }
228277
229- // Don't convert specs blocks to attributes - we'll handle them manually in the string building
230-
231278 // Build the expression using HCL functions
232- configForEach := replaceDynamicBlockReferences (hcl .GetAttrExpr (dConfig .forEach ), nRepSpecs , nSpec )
279+ // Use standardized property name (region_configs) instead of the actual for_each collection
280+ configForEach := fmt .Sprintf ("%s.%s" , nSpec , nConfig )
233281
234282 // Create the inner region_configs body
235283 regionConfigFile := hclwrite .NewEmptyFile ()
236284 regionConfigBody := regionConfigFile .Body ()
237285
238- // Add all attributes generically in alphabetical order
239- attrs := dConfig .content .Body ().Attributes ()
240- attrNames := make ([]string , 0 , len (attrs ))
241- for name := range attrs {
242- attrNames = append (attrNames , name )
243- }
244- slices .Sort (attrNames )
245-
246- for _ , name := range attrNames {
247- attr := attrs [name ]
248- regionConfigBody .SetAttributeRaw (name , hcl .TokensFromExpr (hcl .GetAttrExpr (attr )))
249- }
286+ // Add attributes in a specific order for consistency
287+ orderedAttrs := []string {"priority" , "provider_name" , "region_name" }
288+ addAttributesInOrder (regionConfigBody , dConfig .content .Body ().Attributes (), orderedAttrs )
250289
251290 // Add all blocks generically as objects
252- for _ , block := range dConfig .content .Body ().Blocks () {
253- blockType := block .Type ()
254-
255- // Create a new body for the block
256- blockFile := hclwrite .NewEmptyFile ()
257- blockBody := blockFile .Body ()
258-
259- // Add block attributes in alphabetical order
260- blockAttrs := block .Body ().Attributes ()
261- blockAttrNames := make ([]string , 0 , len (blockAttrs ))
262- for name := range blockAttrs {
263- blockAttrNames = append (blockAttrNames , name )
264- }
265- slices .Sort (blockAttrNames )
266-
267- for _ , attrName := range blockAttrNames {
268- attr := blockAttrs [attrName ]
269- blockBody .SetAttributeRaw (attrName , hcl .TokensFromExpr (hcl .GetAttrExpr (attr )))
270- }
271-
272- regionConfigBody .SetAttributeRaw (blockType , hcl .TokensObject (blockBody ))
273- }
291+ processRegionConfigBlocks (regionConfigBody , dConfig .content .Body ().Blocks ())
274292
275293 // Build the region_configs for expression
276294 regionForExpr := fmt .Sprintf ("for %s in %s :" , nRegion , configForEach )
@@ -306,8 +324,46 @@ func convertDynamicRepSpecsWithDynamicConfig(resourceb *hclwrite.Body, dSpec, dC
306324 return nil
307325 }
308326 // No num_shards, handle like without nested shards
309- // Reuse the block name from the dynamic block that was already obtained
327+ return convertDynamicRepSpecsWithoutNumShards (resourceb , dSpec , dConfig , diskSizeGB , configBlockName )
328+ }
310329
330+ // Helper function to add attributes with transformation
331+ func addAttributesWithTransform (targetBody * hclwrite.Body , sourceAttrs map [string ]* hclwrite.Attribute ,
332+ orderedNames []string , configBlockName string , transformFunc func (string ) string ) {
333+ // Add ordered attributes first
334+ for _ , name := range orderedNames {
335+ if attr , exists := sourceAttrs [name ]; exists {
336+ expr := hcl .GetAttrExpr (attr )
337+ // First replace nested dynamic block references
338+ expr = replaceDynamicBlockReferences (expr , configBlockName , nRegion )
339+ // Then replace outer dynamic block references
340+ expr = replaceDynamicBlockReferences (expr , nRepSpecs , nSpec )
341+ targetBody .SetAttributeRaw (name , hcl .TokensFromExpr (expr ))
342+ }
343+ }
344+
345+ // Add any remaining attributes
346+ for name , attr := range sourceAttrs {
347+ found := false
348+ for _ , orderedName := range orderedNames {
349+ if name == orderedName {
350+ found = true
351+ break
352+ }
353+ }
354+ if ! found {
355+ expr := hcl .GetAttrExpr (attr )
356+ // First replace nested dynamic block references
357+ expr = replaceDynamicBlockReferences (expr , configBlockName , nRegion )
358+ // Then replace outer dynamic block references
359+ expr = replaceDynamicBlockReferences (expr , nRepSpecs , nSpec )
360+ targetBody .SetAttributeRaw (name , hcl .TokensFromExpr (expr ))
361+ }
362+ }
363+ }
364+
365+ func convertDynamicRepSpecsWithoutNumShards (resourceb * hclwrite.Body , dSpec , dConfig dynamicBlock ,
366+ diskSizeGB hclwrite.Tokens , configBlockName string ) error {
311367 // Get zone_name if present
312368 repSpecFile := hclwrite .NewEmptyFile ()
313369 repSpecb := repSpecFile .Body ()
@@ -321,28 +377,22 @@ func convertDynamicRepSpecsWithDynamicConfig(resourceb *hclwrite.Body, dSpec, dC
321377 configFile := hclwrite .NewEmptyFile ()
322378 configb := configFile .Body ()
323379
324- // Copy and transform attributes
325- for name , attr := range dConfig .content .Body ().Attributes () {
326- expr := hcl .GetAttrExpr (attr )
327- // First replace nested dynamic block references
328- expr = replaceDynamicBlockReferences (expr , configBlockName , nRegion )
329- // Then replace outer dynamic block references
330- expr = replaceDynamicBlockReferences (expr , nRepSpecs , nSpec )
331- configb .SetAttributeRaw (name , hcl .TokensFromExpr (expr ))
332- }
380+ // Copy and transform attributes in a specific order for consistency
381+ orderedAttrs := []string {"priority" , "provider_name" , "region_name" }
382+ addAttributesWithTransform (configb , dConfig .content .Body ().Attributes (), orderedAttrs , configBlockName , nil )
333383
334384 // Process blocks and transform their references
335385 for _ , block := range dConfig .content .Body ().Blocks () {
336386 newBlock := configb .AppendNewBlock (block .Type (), block .Labels ())
337387 newBlockb := newBlock .Body ()
338- for name , attr := range block .Body ().Attributes () {
339- expr := hcl .GetAttrExpr (attr )
340- // First replace nested dynamic block references
341- expr = replaceDynamicBlockReferences (expr , configBlockName , nRegion )
342- // Then replace outer dynamic block references
343- expr = replaceDynamicBlockReferences (expr , nRepSpecs , nSpec )
344- newBlockb .SetAttributeRaw (name , hcl .TokensFromExpr (expr ))
388+
389+ // Order attributes for consistency
390+ var attrOrder []string
391+ if block .Type () == "electable_specs" {
392+ attrOrder = []string {"instance_size" , "node_count" }
345393 }
394+
395+ addAttributesWithTransform (newBlockb , block .Body ().Attributes (), attrOrder , configBlockName , nil )
346396 }
347397
348398 // Process specs
@@ -353,22 +403,25 @@ func convertDynamicRepSpecsWithDynamicConfig(resourceb *hclwrite.Body, dSpec, dC
353403 fillSpecOpt (configb , nAnalyticsAutoScaling , nil )
354404
355405 // Build the nested for expression for region_configs
356- configForEach := replaceDynamicBlockReferences (hcl .GetAttrExpr (dConfig .forEach ), nRepSpecs , nSpec )
357- var regionTokens2 hclwrite.Tokens
358- regionTokens2 = append (regionTokens2 , hcl .TokensFromExpr ("[\n " )... )
359- forExpr := fmt .Sprintf (" for %s in %s : " , nRegion , configForEach )
360- regionTokens2 = append (regionTokens2 , hcl .TokensFromExpr (forExpr )... )
361- regionTokens2 = append (regionTokens2 , hcl .TokensObject (configb )... )
362- regionTokens2 = append (regionTokens2 , hcl .TokensFromExpr ("\n ]" )... )
363- repSpecb .SetAttributeRaw (nConfig , regionTokens2 )
364-
365- // Build the for expression without range
366- var tokens hclwrite.Tokens
367- tokens = append (tokens , hcl .TokensFromExpr ("[\n " )... )
368- specForExpr := fmt .Sprintf (" for %s in %s : " , nSpec , hcl .GetAttrExpr (dSpec .forEach ))
369- tokens = append (tokens , hcl .TokensFromExpr (specForExpr )... )
370- tokens = append (tokens , hcl .TokensObject (repSpecb )... )
371- tokens = append (tokens , hcl .TokensFromExpr ("\n ]" )... )
406+ // Use standardized property name (region_configs) instead of the actual for_each collection
407+ configForEach := fmt .Sprintf ("%s.%s" , nSpec , nConfig )
408+
409+ // Build the region_configs for expression
410+ regionForExpr := fmt .Sprintf ("for %s in %s :" , nRegion , configForEach )
411+ regionTokens := hcl .TokensFromExpr (regionForExpr )
412+ regionTokens = append (regionTokens , hcl .TokensObject (configb )... )
413+
414+ repSpecb .SetAttributeRaw (nConfig , hcl .EncloseBracketsNewLines (regionTokens ))
415+
416+ // Build the for expression as an array wrapped in flatten
417+ // Format: flatten([for spec in ... : [ { ... } ] ])
418+ forExpr := fmt .Sprintf ("for %s in %s : [\n " , nSpec , hcl .GetAttrExpr (dSpec .forEach ))
419+ innerTokens := hcl .TokensFromExpr (forExpr )
420+ innerTokens = append (innerTokens , hcl .TokensObject (repSpecb )... )
421+ innerTokens = append (innerTokens , hcl .TokensFromExpr ("\n ]" )... )
422+
423+ // Apply flatten to the entire expression
424+ tokens := hcl .TokensFuncFlatten (innerTokens )
372425
373426 resourceb .RemoveBlock (dSpec .block )
374427 resourceb .SetAttributeRaw (nRepSpecs , tokens )
0 commit comments