Skip to content

Commit

Permalink
hclext: Add schema mode to BodySchema (#201)
Browse files Browse the repository at this point in the history
* Add JustAttributesMode

* Add support SchemaMode in gRPC plugin
  • Loading branch information
wata727 authored Oct 2, 2022
1 parent 1f55995 commit eed3c18
Show file tree
Hide file tree
Showing 9 changed files with 591 additions and 291 deletions.
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

0 comments on commit eed3c18

Please sign in to comment.