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

hclext: Add schema mode to BodySchema #201

Merged
merged 2 commits into from
Oct 2, 2022
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
14 changes: 14 additions & 0 deletions hclext/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,23 @@ import (
"strings"
)

// SchemaMode controls how the body's schema is declared.
//
//go:generate stringer -type=SchemaMode
type SchemaMode int32

const (
// SchemaDefaultMode is a mode for explicitly declaring the structure of attributes and blocks.
SchemaDefaultMode SchemaMode = iota
// SchemaJustAttributesMode is the mode to extract body as attributes.
// In this mode you don't need to declare schema for attributes or blocks.
SchemaJustAttributesMode
)

// BodySchema represents the desired body.
// This structure is designed to have attributes similar to hcl.BodySchema.
type BodySchema struct {
Mode SchemaMode
Attributes []AttributeSchema
Blocks []BlockSchema
}
Expand Down
24 changes: 24 additions & 0 deletions hclext/schemamode_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 21 additions & 2 deletions hclext/structure.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hclext

import (
"fmt"
"reflect"

"github.com/hashicorp/hcl/v2"
Expand Down Expand Up @@ -71,7 +72,16 @@ func Content(body hcl.Body, schema *BodySchema) (*BodyContent, hcl.Diagnostics)
childS[blockS.Type] = blockS.Body
}

content, diags := body.Content(hclS)
content := &hcl.BodyContent{}
var diags hcl.Diagnostics
switch schema.Mode {
case SchemaDefaultMode:
content, diags = body.Content(hclS)
case SchemaJustAttributesMode:
content.Attributes, diags = body.JustAttributes()
default:
panic(fmt.Sprintf("invalid SchemaMode: %s", schema.Mode))
}

ret := &BodyContent{
Attributes: Attributes{},
Expand Down Expand Up @@ -127,7 +137,16 @@ func PartialContent(body hcl.Body, schema *BodySchema) (*BodyContent, hcl.Diagno
childS[blockS.Type] = blockS.Body
}

content, _, diags := body.PartialContent(hclS)
content := &hcl.BodyContent{}
var diags hcl.Diagnostics
switch schema.Mode {
case SchemaDefaultMode:
content, _, diags = body.PartialContent(hclS)
case SchemaJustAttributesMode:
content.Attributes, diags = body.JustAttributes()
default:
panic(fmt.Sprintf("invalid SchemaMode: %s", schema.Mode))
}

ret := &BodyContent{
Attributes: Attributes{},
Expand Down
121 changes: 121 additions & 0 deletions hclext/structure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,127 @@ func TestContent_PartialContent(t *testing.T) {
}
}

func TestContent_JustAttributes(t *testing.T) {
tests := []struct {
Name string
Body *hclsyntax.Body
Schema *BodySchema
Partial bool
Want *BodyContent
DiagCount int
}{
{
Name: "just attributes in the top level",
Body: &hclsyntax.Body{
Attributes: hclsyntax.Attributes{
"foo": &hclsyntax.Attribute{Name: "foo"},
"bar": &hclsyntax.Attribute{Name: "bar"},
"baz": &hclsyntax.Attribute{Name: "baz"},
},
},
Schema: &BodySchema{Mode: SchemaJustAttributesMode},
Want: &BodyContent{
Attributes: Attributes{
"foo": &Attribute{Name: "foo"},
"bar": &Attribute{Name: "bar"},
"baz": &Attribute{Name: "baz"},
},
Blocks: Blocks{},
},
},
{
Name: "just attributes in nested blocks",
Body: &hclsyntax.Body{
Blocks: hclsyntax.Blocks{
&hclsyntax.Block{
Type: "bar",
Body: &hclsyntax.Body{
Attributes: hclsyntax.Attributes{
"foo": &hclsyntax.Attribute{Name: "foo"},
"bar": &hclsyntax.Attribute{Name: "bar"},
"baz": &hclsyntax.Attribute{Name: "baz"},
},
},
},
},
},
Schema: &BodySchema{
Blocks: []BlockSchema{
{
Type: "bar",
Body: &BodySchema{Mode: SchemaJustAttributesMode},
},
},
},
Want: &BodyContent{
Attributes: Attributes{},
Blocks: Blocks{
{
Type: "bar",
Body: &BodyContent{
Attributes: Attributes{
"foo": &Attribute{Name: "foo"},
"bar": &Attribute{Name: "bar"},
"baz": &Attribute{Name: "baz"},
},
Blocks: Blocks{},
},
},
},
},
},
{
Name: "just attributes in body with blocks",
Body: &hclsyntax.Body{
Attributes: hclsyntax.Attributes{
"foo": &hclsyntax.Attribute{Name: "foo"},
"bar": &hclsyntax.Attribute{Name: "bar"},
"baz": &hclsyntax.Attribute{Name: "baz"},
},
Blocks: hclsyntax.Blocks{
&hclsyntax.Block{
Type: "bar",
Body: &hclsyntax.Body{},
},
},
},
Schema: &BodySchema{Mode: SchemaJustAttributesMode},
Want: &BodyContent{
Attributes: Attributes{
"foo": &Attribute{Name: "foo"},
"bar": &Attribute{Name: "bar"},
"baz": &Attribute{Name: "baz"},
},
Blocks: Blocks{},
},
DiagCount: 1, // Unexpected "bar" block; Blocks are not allowed here.
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
var got *BodyContent
var diags hcl.Diagnostics
if test.Partial {
got, diags = PartialContent(test.Body, test.Schema)
} else {
got, diags = Content(test.Body, test.Schema)
}

if len(diags) != test.DiagCount {
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
for _, diag := range diags {
t.Logf(" - %s", diag.Error())
}
}

if diff := cmp.Diff(test.Want, got); diff != "" {
t.Errorf("wrong result\ndiff: %s", diff)
}
})
}
}

func Test_IsEmpty(t *testing.T) {
tests := []struct {
name string
Expand Down
15 changes: 15 additions & 0 deletions plugin/fromproto/fromproto.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,26 @@ func BodySchema(body *proto.BodySchema) *hclext.BodySchema {
}

return &hclext.BodySchema{
Mode: SchemaMode(body.Mode),
Attributes: attributes,
Blocks: blocks,
}
}

// SchemaMode converts proto.SchemaMode to hclext.SchemaMode
func SchemaMode(mode proto.SchemaMode) hclext.SchemaMode {
switch mode {
case proto.SchemaMode_SCHEMA_MODE_UNSPECIFIED:
return hclext.SchemaDefaultMode
case proto.SchemaMode_SCHEMA_MODE_DEFAULT:
return hclext.SchemaDefaultMode
case proto.SchemaMode_SCHEMA_MODE_JUST_ATTRIBUTES:
return hclext.SchemaJustAttributesMode
default:
panic(fmt.Sprintf("invalid SchemaMode: %s", mode))
}
}

// BodyContent converts proto.BodyContent to hclext.BodyContent
func BodyContent(body *proto.BodyContent) (*hclext.BodyContent, hcl.Diagnostics) {
if body == nil {
Expand Down
19 changes: 19 additions & 0 deletions plugin/plugin2host/plugin2host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,25 @@ resource "aws_instance" "foo" {
},
ErrCheck: neverHappend,
},
{
Name: "get content as just attributes",
Args: func() (*hclext.BodySchema, *tflint.GetModuleContentOption) {
return &hclext.BodySchema{Mode: hclext.SchemaJustAttributesMode}, nil
},
ServerImpl: func(schema *hclext.BodySchema, opts tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics) {
file := hclFile("test.tf", `
instance_type = "t2.micro"
volume_size = 10`)
return hclext.Content(file.Body, schema)
},
Want: func(schema *hclext.BodySchema, opts *tflint.GetModuleContentOption) (*hclext.BodyContent, hcl.Diagnostics) {
file := hclFile("test.tf", `
instance_type = "t2.micro"
volume_size = 10`)
return hclext.Content(file.Body, schema)
},
ErrCheck: neverHappend,
},
{
Name: "get content with options",
Args: func() (*hclext.BodySchema, *tflint.GetModuleContentOption) {
Expand Down
Loading