From 7ddc33ae49e318b6cbb3eca2166edacd852738fa Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Thu, 18 Jul 2024 15:30:39 +0200 Subject: [PATCH 1/2] feat: allow overriding existing blocks and attributes for dependent body schemas We couldn't find any cases where the existing tests (and some example codebase) would access the else statement while testing. However, this change could allow e.g. Terraform providers to override for example the lifecycle block if they define such a block in their schema. We have yet to see such cases though :) --- decoder/internal/schemahelper/block_schema.go | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/decoder/internal/schemahelper/block_schema.go b/decoder/internal/schemahelper/block_schema.go index 89be9594..d54552b0 100644 --- a/decoder/internal/schemahelper/block_schema.go +++ b/decoder/internal/schemahelper/block_schema.go @@ -29,29 +29,18 @@ func MergeBlockBodySchemas(block *hcl.Block, blockSchema *schema.BlockSchema) (* depSchema, _, result := NewBlockSchema(blockSchema).DependentBodySchema(block) if result == LookupSuccessful || result == LookupPartiallySuccessful { for name, attr := range depSchema.Attributes { - if _, exists := mergedSchema.Attributes[name]; !exists { - mergedSchema.Attributes[name] = attr - } else { - // Skip duplicate attribute - continue - } + mergedSchema.Attributes[name] = attr } for bType, block := range depSchema.Blocks { - if _, exists := mergedSchema.Blocks[bType]; !exists { - copiedBlock := block.Copy() - // propagate DynamicBlocks extension to any nested blocks - if mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks { - if copiedBlock.Body.Extensions == nil { - copiedBlock.Body.Extensions = &schema.BodyExtensions{} - } - copiedBlock.Body.Extensions.DynamicBlocks = true + copiedBlock := block.Copy() + // propagate DynamicBlocks extension to any nested blocks + if mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks { + if copiedBlock.Body.Extensions == nil { + copiedBlock.Body.Extensions = &schema.BodyExtensions{} } - - mergedSchema.Blocks[bType] = copiedBlock - } else { - // Skip duplicate block type - continue + copiedBlock.Body.Extensions.DynamicBlocks = true } + mergedSchema.Blocks[bType] = copiedBlock } if mergedSchema.Extensions != nil && mergedSchema.Extensions.DynamicBlocks && len(depSchema.Blocks) > 0 { From 0c51e9154b93feb1143824ee25844e5117c61be4 Mon Sep 17 00:00:00 2001 From: Ansgar Mertens Date: Fri, 19 Jul 2024 17:32:41 +0200 Subject: [PATCH 2/2] chore: add test case --- .../schemahelper/dependent_body_test.go | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/decoder/internal/schemahelper/dependent_body_test.go b/decoder/internal/schemahelper/dependent_body_test.go index 5e8c3947..00a97c18 100644 --- a/decoder/internal/schemahelper/dependent_body_test.go +++ b/decoder/internal/schemahelper/dependent_body_test.go @@ -544,6 +544,98 @@ func TestBodySchema_DependentBodySchema_label_lookupUnsorted(t *testing.T) { } } +func TestBodySchema_DependentBodySchema_allows_overriding(t *testing.T) { + testSchema := NewBlockSchema(&schema.BlockSchema{ + Labels: []*schema.LabelSchema{ + { + Name: "type", + IsDepKey: true, + }, + }, + Body: &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "value": { + Constraint: schema.AnyExpression{}, + }, + }, + Blocks: map[string]*schema.BlockSchema{ + "config": { + Description: lang.Markdown("Provider configuration"), + MaxItems: 1, + }, + }, + }, + DependentBody: map[schema.SchemaKey]*schema.BodySchema{ + schema.NewSchemaKey(schema.DependencyKeys{ + Labels: []schema.LabelDependent{ + { + Index: 0, + Value: "specific", + }, + }, + }): { + Attributes: map[string]*schema.AttributeSchema{ + "value": { + Constraint: schema.LiteralType{Type: cty.String}, + }, + }, + Blocks: map[string]*schema.BlockSchema{ + "config": { + Description: lang.Markdown("Provider configuration"), + MaxItems: 1, + Body: &schema.BodySchema{ + Attributes: map[string]*schema.AttributeSchema{ + "extra": { + Constraint: schema.LiteralType{Type: cty.String}, + }, + }, + }, + }, + }, + }, + }, + }) + + block := &hcl.Block{ + Labels: []string{"specific"}, + Body: &hclsyntax.Body{ + Attributes: map[string]*hclsyntax.Attribute{ + "value": { + Name: "value", + Expr: &hclsyntax.LiteralValueExpr{ + Val: cty.StringVal("hello"), + }, + }, + }, + Blocks: []*hclsyntax.Block{ + { + Body: &hclsyntax.Body{ + Attributes: map[string]*hclsyntax.Attribute{ + "extra": { + Name: "extra", + Expr: &hclsyntax.LiteralValueExpr{ + Val: cty.StringVal("world"), + }, + }, + }, + }, + }, + }, + }, + } + + merged, result := MergeBlockBodySchemas(block, testSchema.BlockSchema) + if result != LookupSuccessful { + t.Fatal("expected lookup result to be successful") + } + if merged.Blocks["config"].Body == nil { + t.Fatal("expected to find overridden attribute in merged schema for blocks") + } + if _, ok := merged.Attributes["value"].Constraint.(schema.AnyExpression); ok { + t.Fatal("expected to find overridden attribute in merged schema for attributes") + } +} + var testSchemaWithLabels = NewBlockSchema(&schema.BlockSchema{ Labels: []*schema.LabelSchema{ {