Skip to content

Commit

Permalink
fix: read shortcut fields: documentDescribes and hasFiles (#201)
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Zantow <kzantow@gmail.com>
  • Loading branch information
kzantow committed Mar 6, 2023
1 parent 76e74f6 commit 3ccd09f
Show file tree
Hide file tree
Showing 9 changed files with 460 additions and 22 deletions.
51 changes: 51 additions & 0 deletions spdx/v2/v2_2/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package v2_2

import (
"encoding/json"

"github.com/anchore/go-struct-converter"

"github.com/spdx/tools-golang/spdx/v2/common"
Expand Down Expand Up @@ -77,3 +79,52 @@ func (d *Document) ConvertFrom(_ interface{}) error {
}

var _ converter.ConvertFrom = (*Document)(nil)

func (d *Document) UnmarshalJSON(b []byte) error {
type doc Document
type extras struct {
DocumentDescribes []common.DocElementID `json:"documentDescribes"`
}

var d2 doc
if err := json.Unmarshal(b, &d2); err != nil {
return err
}

var e extras
if err := json.Unmarshal(b, &e); err != nil {
return err
}

*d = Document(d2)

// build relationships for documentDescribes field
for _, id := range e.DocumentDescribes {
d.Relationships = append(d.Relationships, &Relationship{
RefA: common.DocElementID{
ElementRefID: d.SPDXIdentifier,
},
RefB: id,
Relationship: common.TypeRelationshipDescribe,
})
}

// build relationships for package hasFiles field
for _, p := range d.Packages {
for _, f := range p.hasFiles {
d.Relationships = append(d.Relationships, &Relationship{
RefA: common.DocElementID{
ElementRefID: p.PackageSPDXIdentifier,
},
RefB: f,
Relationship: common.TypeRelationshipContains,
})
}

p.hasFiles = nil
}

return nil
}

var _ json.Unmarshaler = (*Document)(nil)
149 changes: 142 additions & 7 deletions spdx/v2/v2_2/json/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,64 @@ import (
"bytes"
"fmt"
"os"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"

"github.com/spdx/tools-golang/json"
"github.com/spdx/tools-golang/spdx/v2/v2_2"
"github.com/spdx/tools-golang/spdx/v2/common"
spdx "github.com/spdx/tools-golang/spdx/v2/v2_2"
"github.com/spdx/tools-golang/spdx/v2/v2_2/example"
)

func TestLoad(t *testing.T) {
want := example.Copy()

want.Relationships = append(want.Relationships, []*spdx.Relationship{
{
RefA: common.DocElementID{ElementRefID: "DOCUMENT"},
RefB: common.DocElementID{ElementRefID: "File"},
Relationship: "DESCRIBES",
},
{
RefA: common.DocElementID{ElementRefID: "DOCUMENT"},
RefB: common.DocElementID{ElementRefID: "Package"},
Relationship: "DESCRIBES",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "CommonsLangSrc"},
Relationship: "CONTAINS",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "JenaLib"},
Relationship: "CONTAINS",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "DoapSource"},
Relationship: "CONTAINS",
},
}...)

file, err := os.Open("../../../../examples/sample-docs/json/SPDXJSONExample-v2.2.spdx.json")
if err != nil {
panic(fmt.Errorf("error opening File: %s", err))
}

var got v2_2.Document
var got spdx.Document
err = json.ReadInto(file, &got)
if err != nil {
t.Errorf("json.parser.Load() error = %v", err)
return
}

if !cmp.Equal(want, got) {
t.Errorf("got incorrect struct after parsing YAML example: %s", cmp.Diff(want, got))
if !cmp.Equal(want, got, cmpopts.IgnoreUnexported(spdx.Package{})) {
t.Errorf("got incorrect struct after parsing YAML example: %s", cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{})))
return
}
}
Expand All @@ -47,15 +79,118 @@ func Test_Write(t *testing.T) {
}

// we should be able to parse what the writer wrote, and it should be identical to the original struct we wrote
var got v2_2.Document
var got spdx.Document
err := json.ReadInto(bytes.NewReader(w.Bytes()), &got)
if err != nil {
t.Errorf("failed to parse written document: %v", err.Error())
return
}

if !cmp.Equal(want, got) {
t.Errorf("got incorrect struct after writing and re-parsing JSON example: %s", cmp.Diff(want, got))
if !cmp.Equal(want, got, cmpopts.IgnoreUnexported(spdx.Package{})) {
t.Errorf("got incorrect struct after writing and re-parsing JSON example: %s", cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{})))
return
}
}

func Test_ShorthandFields(t *testing.T) {
contents := `{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "SPDX-Tools-v2.0",
"documentDescribes": [
"SPDXRef-Container"
],
"packages": [
{
"name": "Container",
"SPDXID": "SPDXRef-Container"
},
{
"name": "Package-1",
"SPDXID": "SPDXRef-Package-1",
"versionInfo": "1.1.1",
"hasFiles": [
"SPDXRef-File-1",
"SPDXRef-File-2"
]
},
{
"name": "Package-2",
"SPDXID": "SPDXRef-Package-2",
"versionInfo": "2.2.2"
}
],
"files": [
{
"fileName": "./f1",
"SPDXID": "SPDXRef-File-1"
},
{
"fileName": "./f2",
"SPDXID": "SPDXRef-File-2"
}
]
}`

doc := spdx.Document{}
err := json.ReadInto(strings.NewReader(contents), &doc)

require.NoError(t, err)

id := func(s string) common.DocElementID {
return common.DocElementID{
ElementRefID: common.ElementID(s),
}
}

require.Equal(t, spdx.Document{
SPDXVersion: spdx.Version,
DataLicense: spdx.DataLicense,
SPDXIdentifier: "DOCUMENT",
DocumentName: "SPDX-Tools-v2.0",
Packages: []*spdx.Package{
{
PackageName: "Container",
PackageSPDXIdentifier: "Container",
},
{
PackageName: "Package-1",
PackageSPDXIdentifier: "Package-1",
PackageVersion: "1.1.1",
},
{
PackageName: "Package-2",
PackageSPDXIdentifier: "Package-2",
PackageVersion: "2.2.2",
},
},
Files: []*spdx.File{
{
FileName: "./f1",
FileSPDXIdentifier: "File-1",
},
{
FileName: "./f2",
FileSPDXIdentifier: "File-2",
},
},
Relationships: []*spdx.Relationship{
{
RefA: id("DOCUMENT"),
RefB: id("Container"),
Relationship: common.TypeRelationshipDescribe,
},
{
RefA: id("Package-1"),
RefB: id("File-1"),
Relationship: common.TypeRelationshipContains,
},
{
RefA: id("Package-1"),
RefB: id("File-2"),
Relationship: common.TypeRelationshipContains,
},
},
}, doc)
}
31 changes: 31 additions & 0 deletions spdx/v2/v2_2/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package v2_2

import (
"encoding/json"

"github.com/spdx/tools-golang/spdx/v2/common"
)

Expand Down Expand Up @@ -113,8 +115,37 @@ type Package struct {
Files []*File `json:"files,omitempty"`

Annotations []Annotation `json:"annotations,omitempty"`

// this field is only used when decoding JSON to translate the hasFiles
// property to relationships
hasFiles []common.DocElementID
}

func (p *Package) UnmarshalJSON(b []byte) error {
type pkg Package
type extras struct {
HasFiles []common.DocElementID `json:"hasFiles"`
}

var p2 pkg
if err := json.Unmarshal(b, &p2); err != nil {
return err
}

var e extras
if err := json.Unmarshal(b, &e); err != nil {
return err
}

*p = Package(p2)

p.hasFiles = e.HasFiles

return nil
}

var _ json.Unmarshaler = (*Package)(nil)

// PackageExternalReference is an External Reference to additional info
// about a Package, as defined in section 7.21 in version 2.2 of the spec.
type PackageExternalReference struct {
Expand Down
44 changes: 37 additions & 7 deletions spdx/v2/v2_2/yaml/yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,59 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"

"github.com/spdx/tools-golang/spdx/v2/v2_2"
"github.com/spdx/tools-golang/spdx/v2/common"
spdx "github.com/spdx/tools-golang/spdx/v2/v2_2"
"github.com/spdx/tools-golang/spdx/v2/v2_2/example"
"github.com/spdx/tools-golang/yaml"
)

func Test_Read(t *testing.T) {
want := example.Copy()

want.Relationships = append(want.Relationships, []*spdx.Relationship{
{
RefA: common.DocElementID{ElementRefID: "DOCUMENT"},
RefB: common.DocElementID{ElementRefID: "File"},
Relationship: "DESCRIBES",
},
{
RefA: common.DocElementID{ElementRefID: "DOCUMENT"},
RefB: common.DocElementID{ElementRefID: "Package"},
Relationship: "DESCRIBES",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "CommonsLangSrc"},
Relationship: "CONTAINS",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "JenaLib"},
Relationship: "CONTAINS",
},
{
RefA: common.DocElementID{ElementRefID: "Package"},
RefB: common.DocElementID{ElementRefID: "DoapSource"},
Relationship: "CONTAINS",
},
}...)

file, err := os.Open("../../../../examples/sample-docs/yaml/SPDXYAMLExample-2.2.spdx.yaml")
if err != nil {
panic(fmt.Errorf("error opening File: %s", err))
}

var got v2_2.Document
var got spdx.Document
err = yaml.ReadInto(file, &got)
if err != nil {
t.Errorf("yaml.Read() error = %v", err)
return
}

if !cmp.Equal(want, got) {
t.Errorf("got incorrect struct after parsing YAML example: %s", cmp.Diff(want, got))
if !cmp.Equal(want, got, cmpopts.IgnoreUnexported(spdx.Package{})) {
t.Errorf("got incorrect struct after parsing YAML example: %s", cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{})))
return
}
}
Expand All @@ -46,15 +76,15 @@ func Test_Write(t *testing.T) {
}

// we should be able to parse what the writer wrote, and it should be identical to the original handwritten struct
var got v2_2.Document
var got spdx.Document
err := yaml.ReadInto(bytes.NewReader(w.Bytes()), &got)
if err != nil {
t.Errorf("failed to parse written document: %v", err.Error())
return
}

if !cmp.Equal(want, got) {
t.Errorf("got incorrect struct after writing and re-parsing YAML example: %s", cmp.Diff(want, got))
if !cmp.Equal(want, got, cmpopts.IgnoreUnexported(spdx.Package{})) {
t.Errorf("got incorrect struct after writing and re-parsing YAML example: %s", cmp.Diff(want, got, cmpopts.IgnoreUnexported(spdx.Package{})))
return
}
}

0 comments on commit 3ccd09f

Please sign in to comment.