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

Merge JSON saving into main #104

Merged
merged 36 commits into from Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2d6415b
JSONsaver : lay down initia setup of jsonsaver
specter25 Jul 8, 2021
45a2a52
jsonSaver : saved other licenses to json
specter25 Jul 8, 2021
5704209
Jsonsaver : Saved document annotations from struct to json
specter25 Jul 9, 2021
9d1037e
JsonSaver : saved document describes from spdx struct to json
specter25 Jul 9, 2021
9787338
Jsonsaver : saved packages and relationships from spdx to json
specter25 Jul 10, 2021
9922fe7
Jsonsaver : save files from spdx strict to json
specter25 Jul 10, 2021
91fb849
jsonsaver : Save snippets from spdx struct to json
specter25 Jul 10, 2021
625fa97
jsonsaver : Parse Reviews from spdx struct to json
specter25 Jul 10, 2021
f14c07d
JsonSaver : Write bytes to io.Writer while saving json
specter25 Jul 14, 2021
cab3ae1
Jsonsaver : Fix tag names fro reverse compatibility
specter25 Jul 15, 2021
4012229
Jsonparser : fix bug while parsiing creators
specter25 Jul 15, 2021
7aab873
Jsonsaver : Fix return types of relationship parser
specter25 Jul 15, 2021
08fb06a
Jsonsaver : Simplify json saving without maintaining the order
specter25 Jul 16, 2021
8d5f3f5
Examples : Write desxription of jsonsaver in the Readme
specter25 Jul 16, 2021
d47d0c2
Examples : Write description of jsonsaver example in readme
specter25 Jul 16, 2021
668152c
Merge pull request #92 from specter25/jsonparser1
swinslow Jul 16, 2021
f64669f
Jsonsaver : minor fixes regarding code semantics
specter25 Jul 16, 2021
7616a1e
Jsonsaver : creation info unit tests written
specter25 Jul 20, 2021
8176ed5
Jsonsaver : Write unit tests for all render functions
specter25 Jul 21, 2021
6995a8a
Jsonsaver : Fix checksum unit tests in case of files and packages
specter25 Jul 21, 2021
81c9631
jsonsaver : increase test coverage
specter25 Jul 21, 2021
1896bb6
Jsonsaver : increase test coverage
specter25 Jul 21, 2021
2d2902a
Jsonsaver : add complete saver test
specter25 Jul 21, 2021
3ac2221
Merge branch 'json' of github.com:spdx/tools-golang into jsonparser1
specter25 Jul 21, 2021
de2a8c9
Jsonsaver : add complete functionslity tests
specter25 Jul 24, 2021
81c7677
Jsonsaver : Package level tests added
specter25 Jul 24, 2021
1a9690f
JsonParser : increase test coverage of files, creationinfo and annota…
specter25 Jul 27, 2021
1bc87f7
Examples : add example of jsonloader and remove existing conversion e…
specter25 Aug 6, 2021
25bc95b
Merge pull request #94 from specter25/jsonparser1
swinslow Aug 6, 2021
dbd63ea
Jsonsaver : maintain order while converting maps to json arrays
specter25 Aug 10, 2021
f679856
JsonParser : JsonParser test coverage increased
specter25 Aug 13, 2021
0faf540
Merge pull request #97 from specter25/jsonparser1
swinslow Aug 13, 2021
87a00cd
Jsonsaver : order maintained while parsing snippets
specter25 Aug 13, 2021
e26f08d
Jsonsaver : Create a temporary storage for files instead of modifying…
specter25 Aug 15, 2021
9afa5fe
Docs : Add docs for jsonparser and jsonsaver
specter25 Aug 16, 2021
1d27f44
Merge pull request #98 from specter25/jsonparser1
swinslow Aug 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/jsonloader.md
@@ -0,0 +1,24 @@
SPDX-License-Identifier: CC-BY-4.0

## Working

A UnmarshallJSON function on the spdx.Document2_2 struct is defined so that when the JSON is unmarshalled in it the function is called and we can implement the process in a custom way . Then a new map[string]interface{} is deifined which temporarily holds the unmarshalled json . The map is then parsed into the spdx.Document2_2 using functions defined for it’s different sections .

JSON → map[string]interface{} → spdx.Document2_2

## Some Key Points

- The packages have a property "hasFiles" defined in the schema which is an array of the SPDX Identifiers of the files of that pacakge . The parses first parses all the files into the Unpackaged files map of the document and then when it parses the packages , it removes the respective files from the unpackaged files map and places it inside the files map of that package .

- The snippets have a property "snippetFromFile" which has the SPDX identiifer of the file to which the snippet is related . Thus the snippets require the files to be parsed before them . Then the snippets are parsed one by one and inserted into the respective files using this property .


The json file loader in `package jsonloader` makes the following assumptions:


### Order of appearance of the properties
* The parser does not make any pre-assumptions based on the order in which the properties appear .


### Annotations
* The json spdx schema does not define the SPDX Identifier property for the annotation object . The parser assumes the spdx Identifier of the parent property of the currently being parsed annotation array to be the SPDX Identifer for all the annotation objects of that array.
28 changes: 28 additions & 0 deletions docs/jsonsaver.md
@@ -0,0 +1,28 @@
SPDX-License-Identifier: CC-BY-4.0

## Working

The spdx document is converted to map[string]interface{} and then the entire map is converted to json using a single json Marshall function call . The saver uses a tempoarary storage to store all the files (Paackaged and Unpackaged) together in a single data structure in order to comply with the json schema defined by spdx .

spdx.Document2_2 → map[string]interface{} → JSON

## Some Key Points

- The packages have a property "hasFiles" defined in the schema which is an array of the SPDX Identifiers of the files of that pacakge . The saver iterates through the files of a package and inserted all the SPDX Identifiers of the files in the "hasFiles" array . In addition it adds the file to a temporary storage map to store all the files of the entire document at a single place .

- The files require the packages to be saved before them in order to ensure that the packaged files are added to the temporary storage before the files are saved .

- The snippets are saved after the files and a property "snippetFromFile" identifies the file of the snippets.

The json file loader in `package jsonsaver` makes the following assumptions:


### Order of appearance of the properties
* The saver does not make any pre-assumptions based on the order in which the properties are saved .


### Annotations
* The json spdx schema does not define the SPDX Identifier property for the annotation object . The saver inserts the annotation inside the element who spdx identifier mathches the annotation SPDX identifier .

### Indentation
* The jsonsaver uses the marshall indent function with "" as he prefix and "\t" as the indent character , passed as funtion parameters .
55 changes: 55 additions & 0 deletions examples/10-jsonloader/example_json_loader.go
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later

// Example for: *jsonparser2v2*

// This example demonstrates loading an SPDX json from disk into memory,
// and then logging out some attributes to the console .

package main

import (
"fmt"
"os"
"strings"

"github.com/spdx/tools-golang/jsonloader"
)

func main() {

// check that we've received the right number of arguments
args := os.Args
if len(args) != 3 {
fmt.Printf("Usage: %v <spdx-file-in> <spdx-file-out>\n", args[0])
fmt.Printf(" Load SPDX 2.2 tag-value file <spdx-file-in>, and\n")
fmt.Printf(" save it out to <spdx-file-out>.\n")
return
}

// open the SPDX file
fileIn := args[1]
r, err := os.Open(fileIn)
if err != nil {
fmt.Printf("Error while opening %v for reading: %v", fileIn, err)
return
}
defer r.Close()

// try to load the SPDX file's contents as a json file, version 2.2
doc, err := jsonloader.Load2_2(r)
if err != nil {
fmt.Printf("Error while parsing %v: %v", args[1], err)
return
}

// if we got here, the file is now loaded into memory.
fmt.Printf("Successfully loaded %s\n", args[1])

fmt.Println(strings.Repeat("=", 80))
fmt.Println("Some Attributes of the Document:")
fmt.Printf("Document Name: %s\n", doc.CreationInfo.DocumentName)
fmt.Printf("DataLicense: %s\n", doc.CreationInfo.DataLicense)
fmt.Printf("Document NameSpace: %s\n", doc.CreationInfo.DocumentNamespace)
fmt.Printf("SPDX Document Version: %s\n", doc.CreationInfo.SPDXVersion)
fmt.Println(strings.Repeat("=", 80))
}
68 changes: 68 additions & 0 deletions examples/9-tvtojson/exampletvtojson.go
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later

// Example for: *tvloader*, *jsonsaver*

// This example demonstrates loading an SPDX tag-value file from disk into memory,
// and re-saving it to a different json file on disk.

package main

import (
"fmt"
"os"

"github.com/spdx/tools-golang/jsonsaver"
"github.com/spdx/tools-golang/tvloader"
)

func main() {

// check that we've received the right number of arguments
args := os.Args
if len(args) != 3 {
fmt.Printf("Usage: %v <spdx-file-in> <spdx-file-out>\n", args[0])
fmt.Printf(" Load SPDX 2.2 tag-value file <spdx-file-in>, and\n")
fmt.Printf(" save it out to <spdx-file-out>.\n")
return
}

// open the SPDX file
fileIn := args[1]
r, err := os.Open(fileIn)
if err != nil {
fmt.Printf("Error while opening %v for reading: %v", fileIn, err)
return
}
defer r.Close()

// try to load the SPDX file's contents as a tag-value file, version 2.2
doc, err := tvloader.Load2_2(r)
if err != nil {
fmt.Printf("Error while parsing %v: %v", args[1], err)
return
}

// if we got here, the file is now loaded into memory.
fmt.Printf("Successfully loaded %s\n", args[1])

// we can now save it back to disk, using jsonsaver.

// create a new file for writing
fileOut := args[2]
w, err := os.Create(fileOut)
if err != nil {
fmt.Printf("Error while opening %v for writing: %v", fileOut, err)
return
}
defer w.Close()

// try to save the document to disk as an SPDX json file, version 2.2
err = jsonsaver.Save2_2(doc, w)
if err != nil {
fmt.Printf("Error while saving %v: %v", fileOut, err)
return
}

// it worked
fmt.Printf("Successfully saved %s\n", fileOut)
}
16 changes: 15 additions & 1 deletion examples/README.md
Expand Up @@ -64,9 +64,23 @@ the same identifier in both documents.
This example demonstrates loading an SPDX rdf file from disk into memory
and then printing the corresponding spdx struct for the document.

## 8-jsonloader
## 8-jsontotv

*jsonloader*, *tvsaver*

This example demonstrates loading an SPDX json from disk into memory
and then re-saving it to a different file on disk in tag-value format.

## 9-tvtojson

*jsonsaver*, *tvloader*

This example demonstrates loading an SPDX tag-value from disk into memory
and then re-saving it to a different file on disk in json format.

## 10-jsonloader

*jsonloader*

This example demonstrates loading an SPDX json from disk into memory
and then logging some of the attributes to the console.
11 changes: 10 additions & 1 deletion jsonloader/jsonloader_test.go
Expand Up @@ -3,6 +3,7 @@
package jsonloader

import (
"bytes"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -45,6 +46,14 @@ func TestLoad2_2(t *testing.T) {
},
wantErr: false,
},
{
name: "fail - invalidjson ",
args: args{
content: bytes.NewReader([]byte(`{"Hello":"HI",}`)),
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -53,7 +62,7 @@ func TestLoad2_2(t *testing.T) {
t.Errorf("Load2_2() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got.CreationInfo, tt.want.CreationInfo) {
if !tt.wantErr && !reflect.DeepEqual(got.CreationInfo, tt.want.CreationInfo) {
t.Errorf("Load2_2() = %v, want %v", got.CreationInfo, tt.want.CreationInfo)
}
})
Expand Down
58 changes: 54 additions & 4 deletions jsonloader/parser2v2/parse_annotations_test.go
Expand Up @@ -31,6 +31,26 @@ func TestJSONSpdxDocument_parseJsonAnnotations2_2(t *testing.T) {
} ]
}
`)
data2 := []byte(`{
"annotations" : [ {
"annotationDate" : "2010-02-10T00:00:00Z",
"annotationType" : "REVIEW",
"annotator" : "Person: Joe Reviewer",
"comment" : "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses",
"Hello":"hellp"
}]
}
`)
data3 := []byte(`{
"annotations" : [ {
"annotationDate" : "2010-02-10T00:00:00Z",
"annotationType" : "REVIEW",
"annotator" : "Fasle: Joe Reviewer",
"comment" : "This is just an example. Some of the non-standard licenses look like they are actually BSD 3 clause licenses",
"Hello":"hellp"
}]
}
`)

annotationstest1 := []*spdx.Annotation2_2{
{
Expand Down Expand Up @@ -60,7 +80,12 @@ func TestJSONSpdxDocument_parseJsonAnnotations2_2(t *testing.T) {
}

var specs JSONSpdxDocument
var specs2 JSONSpdxDocument
var specs3 JSONSpdxDocument

json.Unmarshal(data, &specs)
json.Unmarshal(data2, &specs2)
json.Unmarshal(data3, &specs3)

type args struct {
key string
Expand Down Expand Up @@ -88,16 +113,41 @@ func TestJSONSpdxDocument_parseJsonAnnotations2_2(t *testing.T) {
want: annotationstest1,
wantErr: false,
},
{
name: "failure test - invaid creator type",
spec: specs2,
args: args{
key: "annotations",
value: specs2["annotations"],
doc: &spdxDocument2_2{},
SPDXElementID: spdx.DocElementID{DocumentRefID: "", ElementRefID: "DOCUMENT"},
},
want: nil,
wantErr: true,
},
{
name: "failure test - invalid tag",
spec: specs3,
args: args{
key: "annotations",
value: specs3["annotations"],
doc: &spdxDocument2_2{},
SPDXElementID: spdx.DocElementID{DocumentRefID: "", ElementRefID: "DOCUMENT"},
},
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.spec.parseJsonAnnotations2_2(tt.args.key, tt.args.value, tt.args.doc, tt.args.SPDXElementID); (err != nil) != tt.wantErr {
t.Errorf("JSONSpdxDocument.parseJsonAnnotations2_2() error = %v, wantErr %v", err, tt.wantErr)
}

for i := 0; i < len(tt.want); i++ {
if !reflect.DeepEqual(tt.args.doc.Annotations[i], tt.want[i]) {
t.Errorf("Load2_2() = %v, want %v", tt.args.doc.Annotations[i], tt.want[i])
if !tt.wantErr {
for i := 0; i < len(tt.want); i++ {
if !reflect.DeepEqual(tt.args.doc.Annotations[i], tt.want[i]) {
t.Errorf("Load2_2() = %v, want %v", tt.args.doc.Annotations[i], tt.want[i])
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions jsonloader/parser2v2/parse_creation_info.go
Expand Up @@ -79,9 +79,9 @@ func parseCreators(creators interface{}, ci *spdx.CreationInfo2_2) error {
}
switch subkey {
case "Person":
ci.CreatorPersons = append(ci.CreatorPersons, strings.TrimSuffix(subvalue, " ()"))
ci.CreatorPersons = append(ci.CreatorPersons, subvalue)
case "Organization":
ci.CreatorOrganizations = append(ci.CreatorOrganizations, strings.TrimSuffix(subvalue, " ()"))
ci.CreatorOrganizations = append(ci.CreatorOrganizations, subvalue)
case "Tool":
ci.CreatorTools = append(ci.CreatorTools, subvalue)
default:
Expand Down