Skip to content

Commit

Permalink
Merge pull request #18 from akihiro17/dont-escape-html-characters
Browse files Browse the repository at this point in the history
Don't escape html characters
  • Loading branch information
akihiro17 authored Jul 21, 2023
2 parents 7ba552f + dc390be commit b719045
Show file tree
Hide file tree
Showing 11 changed files with 396 additions and 61 deletions.
12 changes: 11 additions & 1 deletion cmd/terraform-j2md/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
package main

import (
"flag"
"fmt"
"os"

"github.com/reproio/terraform-j2md/internal/terraform"
)

var (
escapeHTML = true
)

func main() {
noEscapeHTML := flag.Bool("no-escape-html", false, "prevent <, >, and & from being escaped in JSON strings")
flag.Parse()
if *noEscapeHTML {
escapeHTML = false
}
os.Exit(run())
}

Expand All @@ -17,7 +27,7 @@ func run() int {
fmt.Fprintf(os.Stderr, "cannot parse input as Terraform plan JSON: %v", err)
return 1
}
if err = planData.Render(os.Stdout); err != nil {
if err = planData.Render(os.Stdout, escapeHTML); err != nil {
fmt.Fprintf(os.Stderr, "cannot render: %v", err)
return 1
}
Expand Down
34 changes: 31 additions & 3 deletions internal/terraform/plan.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package terraform

import (
"bytes"
"encoding/json"
"fmt"
"github.com/hashicorp/terraform-json/sanitize"
Expand Down Expand Up @@ -51,12 +52,18 @@ type ResourceChangeData struct {
ResourceChange *tfjson.ResourceChange
}

type Config struct {
EscapeHTML bool
}

var config Config

func (r ResourceChangeData) GetUnifiedDiffString() (string, error) {
before, err := json.MarshalIndent(r.ResourceChange.Change.Before, "", " ")
before, err := r.marshalChangeBefore()
if err != nil {
return "", fmt.Errorf("invalid resource changes (before): %w", err)
}
after, err := json.MarshalIndent(r.ResourceChange.Change.After, "", " ")
after, err := r.marshalChangeAfter()
if err != nil {
return "", fmt.Errorf("invalid resource changes (after) : %w", err)
}
Expand Down Expand Up @@ -85,6 +92,26 @@ func (r ResourceChangeData) Header() string {
}
}

func (r ResourceChangeData) marshalChangeBefore() ([]byte, error) {
return r.marshalChange(r.ResourceChange.Change.Before)
}

func (r ResourceChangeData) marshalChangeAfter() ([]byte, error) {
return r.marshalChange(r.ResourceChange.Change.After)
}

func (r ResourceChangeData) marshalChange(v any) ([]byte, error) {
var buffer bytes.Buffer
enc := json.NewEncoder(&buffer)
enc.SetIndent("", " ")
enc.SetEscapeHTML(config.EscapeHTML)
err := enc.Encode(v)
if err != nil {
return nil, err
}
return buffer.Bytes(), nil
}

func (r ResourceChangeData) HeaderSuffix() string {
switch {
case r.ResourceChange.Change.Actions.Create():
Expand All @@ -99,7 +126,8 @@ func (r ResourceChangeData) HeaderSuffix() string {
return ""
}

func (plan *PlanData) Render(w io.Writer) error {
func (plan *PlanData) Render(w io.Writer, escapeHTML bool) error {
config.EscapeHTML = escapeHTML
funcMap := template.FuncMap{
"codeFence": func() string {
return "````````"
Expand Down
140 changes: 93 additions & 47 deletions test/plan_test/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,54 +48,100 @@ func Test_newPlanData(t *testing.T) {
}

func Test_render(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
{name: "no_changes", wantErr: false},
{name: "single_add", wantErr: false},
{name: "single_change", wantErr: false},
{name: "single_destroy", wantErr: false},
{name: "single_replace", wantErr: false},
{name: "all_types_mixed", wantErr: false},
{name: "aws_sample", wantErr: false},
{name: "iam_policy", wantErr: false},
{name: "include_code_fence", wantErr: false},
{name: "include_module", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputFilePath := testDataPath(tt.name, "show.json")
file, err := os.Open(inputFilePath)
if err != nil {
t.Errorf("cannot open input file: %s", inputFilePath)
return
}
defer file.Close()
t.Run("escape HTML characters", func(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
{name: "no_changes", wantErr: false},
{name: "single_add", wantErr: false},
{name: "single_change", wantErr: false},
{name: "single_destroy", wantErr: false},
{name: "single_replace", wantErr: false},
{name: "all_types_mixed", wantErr: false},
{name: "aws_sample", wantErr: false},
{name: "iam_policy", wantErr: false},
{name: "include_code_fence", wantErr: false},
{name: "include_module", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputFilePath := testDataPath(tt.name, "show.json")
file, err := os.Open(inputFilePath)
if err != nil {
t.Errorf("cannot open input file: %s", inputFilePath)
return
}
defer file.Close()

plan, err := terraform.NewPlanData(file)
if err != nil {
t.Errorf("cannot parse JSON as plan: %v", err)
return
}
plan, err := terraform.NewPlanData(file)
if err != nil {
t.Errorf("cannot parse JSON as plan: %v", err)
return
}

got := bytes.Buffer{}
err = plan.Render(&got)
if (err != nil) != tt.wantErr {
t.Errorf("render() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := bytes.Buffer{}
err = plan.Render(&got, true)
if (err != nil) != tt.wantErr {
t.Errorf("render() error = %v, wantErr %v", err, tt.wantErr)
return
}

expectedFilePath := testDataPath(tt.name, "expected.md")
expected, err := os.ReadFile(expectedFilePath)
if err != nil {
t.Errorf("cannot open expected file: %s", expectedFilePath)
return
}
if got.String() != string(expected) {
t.Errorf("render() = %v, want %v", got.String(), string(expected))
return
}
})
}
expectedFilePath := testDataPath(tt.name, "expected.md")
expected, err := os.ReadFile(expectedFilePath)
if err != nil {
t.Errorf("cannot open expected file: %s", expectedFilePath)
return
}
if got.String() != string(expected) {
t.Errorf("render() = %v, want %v", got.String(), string(expected))
return
}
})
}
})

t.Run("not escape HTML characters", func(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
{name: "special_characters", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputFilePath := testDataPath(tt.name, "show.json")
file, err := os.Open(inputFilePath)
if err != nil {
t.Errorf("cannot open input file: %s", inputFilePath)
return
}
defer file.Close()

plan, err := terraform.NewPlanData(file)
if err != nil {
t.Errorf("cannot parse JSON as plan: %v", err)
return
}

got := bytes.Buffer{}
err = plan.Render(&got, false)
if (err != nil) != tt.wantErr {
t.Errorf("render() error = %v, wantErr %v", err, tt.wantErr)
return
}

expectedFilePath := testDataPath(tt.name, "expected.md")
expected, err := os.ReadFile(expectedFilePath)
if err != nil {
t.Errorf("cannot open expected file: %s", expectedFilePath)
return
}
if got.String() != string(expected) {
t.Errorf("render() = %v, want %v", got.String(), string(expected))
return
}
})
}
})
}
9 changes: 6 additions & 3 deletions test/testdata/all_types_mixed/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,36 @@

````````diff
# env_variable.test2 will be updated in-place
@@ -1,5 +1,5 @@
@@ -1,6 +1,6 @@
{
"id": "test2",
- "name": "test2",
+ "name": "test2_changed",
"value": "REDACTED_SENSITIVE"
}

````````

````````diff
# env_variable.test3 will be destroyed
@@ -1,5 +1 @@
@@ -1,6 +1,2 @@
-{
- "id": "test3",
- "name": "test3",
- "value": "REDACTED_SENSITIVE"
-}
+null

````````

````````diff
# env_variable.test5 will be created
@@ -1 +1,3 @@
@@ -1,2 +1,4 @@
-null
+{
+ "name": "test5"
+}

````````

````````diff
Expand Down
9 changes: 6 additions & 3 deletions test/testdata/aws_sample/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

````````diff
# aws_instance.test will be destroyed
@@ -1,92 +1 @@
@@ -1,93 +1,2 @@
-{
- "ami": "ami-cbf90ecb",
- "arn": "arn:aws:ec2:ap-northeast-1:999999999999:instance/i-0ecc384fa6f8d0623",
Expand Down Expand Up @@ -106,11 +106,12 @@
- ]
-}
+null

````````

````````diff
# aws_route_table.public-route will be created
@@ -1 +1,22 @@
@@ -1,2 +1,23 @@
-null
+{
+ "route": [
Expand All @@ -134,16 +135,18 @@
+ "timeouts": null,
+ "vpc_id": "vpc-0c08ee65bf93a360f"
+}

````````

````````diff
# aws_route_table_association.puclic-a will be created
@@ -1 +1,4 @@
@@ -1,2 +1,5 @@
-null
+{
+ "gateway_id": null,
+ "subnet_id": "subnet-0342dca4d2a611266"
+}

````````

````````diff
Expand Down
3 changes: 2 additions & 1 deletion test/testdata/include_module/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

````````diff
# module.test1.env_variable.test1 will be created
@@ -1 +1,3 @@
@@ -1,2 +1,4 @@
-null
+{
+ "name": "test1"
+}

````````

</details>
3 changes: 2 additions & 1 deletion test/testdata/single_add/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

````````diff
# null_resource.foo will be created
@@ -1 +1,3 @@
@@ -1,2 +1,4 @@
-null
+{
+ "triggers": null
+}

````````

</details>
3 changes: 2 additions & 1 deletion test/testdata/single_change/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

````````diff
# env_variable.test1 will be updated in-place
@@ -1,5 +1,5 @@
@@ -1,6 +1,6 @@
{
"id": "test1",
- "name": "test1",
+ "name": "test1_changed",
"value": "REDACTED_SENSITIVE"
}

````````

</details>
3 changes: 2 additions & 1 deletion test/testdata/single_destroy/expected.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@

````````diff
# null_resource.foo will be destroyed
@@ -1,4 +1 @@
@@ -1,5 +1,2 @@
-{
- "id": "7047514762471223910",
- "triggers": null
-}
+null

````````

</details>
Loading

0 comments on commit b719045

Please sign in to comment.