Add "pretty" json configuration and change default behavior to be space-efficient (#2275)

* expose underlying format options

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* remove escape html options and address PR feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* incorporate PR feedback

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix cli test

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2023-11-20 15:29:34 +00:00
committed by GitHub
parent 7cfb5f630a
commit 1676934c63
31 changed files with 723 additions and 204 deletions
+7 -2
View File
@@ -14,6 +14,7 @@ const ID sbom.FormatID = "syft-json"
type EncoderConfig struct {
Legacy bool // transform the output to the legacy syft-json format (pre v1.0 changes, enumerated in the README.md)
Pretty bool // don't include spaces and newlines; same as jq -c
}
type encoder struct {
@@ -37,6 +38,7 @@ func NewFormatEncoderWithConfig(cfg EncoderConfig) (sbom.FormatEncoder, error) {
func DefaultEncoderConfig() EncoderConfig {
return EncoderConfig{
Legacy: false,
Pretty: false,
}
}
@@ -59,9 +61,12 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
doc := ToFormatModel(s, e.cfg)
enc := json.NewEncoder(writer)
// prevent > and < from being escaped in the payload
enc.SetEscapeHTML(false)
enc.SetIndent("", " ")
if e.cfg.Pretty {
enc.SetIndent("", " ")
}
return enc.Encode(&doc)
}
+69 -2
View File
@@ -1,9 +1,14 @@
package syftjson
import (
"bytes"
"flag"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
stereoFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
@@ -31,12 +36,69 @@ func TestDefaultNameAndVersion(t *testing.T) {
}
}
func TestPrettyOutput(t *testing.T) {
run := func(opt bool) string {
enc, err := NewFormatEncoderWithConfig(EncoderConfig{
Pretty: opt,
})
require.NoError(t, err)
dir := t.TempDir()
s := testutil.DirectoryInput(t, dir)
var buffer bytes.Buffer
err = enc.Encode(&buffer, s)
require.NoError(t, err)
return strings.TrimSpace(buffer.String())
}
t.Run("pretty", func(t *testing.T) {
actual := run(true)
assert.Contains(t, actual, "\n")
})
t.Run("compact", func(t *testing.T) {
actual := run(false)
assert.NotContains(t, actual, "\n")
})
}
func TestEscapeHTML(t *testing.T) {
dir := t.TempDir()
s := testutil.DirectoryInput(t, dir)
s.Artifacts.Packages.Add(pkg.Package{
Name: "<html-package>",
})
// by default we do not escape HTML
t.Run("default", func(t *testing.T) {
cfg := DefaultEncoderConfig()
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
var buffer bytes.Buffer
err = enc.Encode(&buffer, s)
require.NoError(t, err)
actual := buffer.String()
assert.Contains(t, actual, "<html-package>")
assert.NotContains(t, actual, "\\u003chtml-package\\u003e")
})
}
func TestDirectoryEncoder(t *testing.T) {
cfg := DefaultEncoderConfig()
cfg.Pretty = true
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
dir := t.TempDir()
testutil.AssertEncoderAgainstGoldenSnapshot(t,
testutil.EncoderSnapshotTestConfig{
Subject: testutil.DirectoryInput(t, dir),
Format: NewFormatEncoder(),
Format: enc,
UpdateSnapshot: *updateSnapshot,
PersistRedactionsInSnapshot: true,
IsJSON: true,
@@ -46,6 +108,11 @@ func TestDirectoryEncoder(t *testing.T) {
}
func TestImageEncoder(t *testing.T) {
cfg := DefaultEncoderConfig()
cfg.Pretty = true
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
testImage := "image-simple"
testutil.AssertEncoderAgainstGoldenImageSnapshot(t,
testutil.ImageSnapshotTestConfig{
@@ -54,7 +121,7 @@ func TestImageEncoder(t *testing.T) {
},
testutil.EncoderSnapshotTestConfig{
Subject: testutil.ImageInput(t, testImage, testutil.FromSnapshot()),
Format: NewFormatEncoder(),
Format: enc,
UpdateSnapshot: *updateSnapshot,
PersistRedactionsInSnapshot: true,
IsJSON: true,