aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspecter25 <ujjwalcoding012@gmail.com>2021-06-12 18:22:28 +0530
committerspecter25 <ujjwalcoding012@gmail.com>2021-06-12 18:22:28 +0530
commit0a0e69fc25d9b22b756fcd3c26725d85935b8697 (patch)
tree92238de2d67dd51fc51cce9b1c12eb9ca62d3bf3
parent8e09d22f514a2eeee9a3044fb47d0cb6dbc74a6a (diff)
downloadspdx-tools-0a0e69fc25d9b22b756fcd3c26725d85935b8697.tar.gz
Setup jsonparser initial config and parse creationInfo of spdx doc
- setup parser initial config - parsed creation info - wrote tests Signed-off-by: specter25 <ujjwalcoding012@gmail.com>
-rw-r--r--jsonloader2v2/jsonloader.go68
-rw-r--r--jsonloader2v2/parse_creation_info.go122
-rw-r--r--jsonloader2v2/parse_creation_info_test.go167
-rw-r--r--jsonloader2v2/types.go7
-rw-r--r--jsonloader2v2/util.go100
5 files changed, 464 insertions, 0 deletions
diff --git a/jsonloader2v2/jsonloader.go b/jsonloader2v2/jsonloader.go
new file mode 100644
index 0000000..7a03037
--- /dev/null
+++ b/jsonloader2v2/jsonloader.go
@@ -0,0 +1,68 @@
+// Package jsonloader is used to load and parse SPDX JSON documents
+// into tools-golang data structures.
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package jsonloader2v2
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+//TODO : return spdx.Document2_2
+func Load2_2(content []byte) (*spdxDocument2_2, error) {
+ // check whetehr the Json is valid or not
+ if !json.Valid(content) {
+ return nil, fmt.Errorf("%s", "Invalid JSON Specification")
+ }
+ result := spdxDocument2_2{}
+ // unmarshall the json into the result struct
+ err := json.Unmarshal(content, &result)
+ if err != nil {
+ return nil, fmt.Errorf("%s", err)
+ }
+ return &result, nil
+}
+
+func (doc *spdxDocument2_2) UnmarshalJSON(data []byte) error {
+ var specs JSONSpdxDocument
+ //unmarshall the json into the intermediate stricture map[string]interface{}
+ err := json.Unmarshal(data, &specs)
+ if err != nil {
+ return err
+ }
+ // parse the data from the intermediate structure to the spdx.Document2_2{}
+ err = specs.newDocument(doc)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (spec JSONSpdxDocument) newDocument(doc *spdxDocument2_2) error {
+ // raneg through all the keys in the map and send them to appropriate arsing functions
+ for key, val := range spec {
+ switch key {
+ case "dataLicense", "spdxVersion", "SPDXID", "documentNamespace", "name", "comment", "creationInfo", "externalDocumentRefs":
+ err := spec.parseJsonCreationInfo2_2(key, val, doc)
+ if err != nil {
+ return err
+ }
+ // case "annotations":
+ // err := spec.parseJsonAnnotations2_2(key, val, doc)
+ // if err != nil {
+ // return err
+ // }
+ // case "relationships":
+ // err := spec.parseJsonRelationships2_2(key, val, doc)
+ // if err != nil {
+ // return err
+ // }
+ default:
+ return fmt.Errorf("unrecognized key here %v", key)
+
+ }
+
+ }
+ return nil
+}
diff --git a/jsonloader2v2/parse_creation_info.go b/jsonloader2v2/parse_creation_info.go
new file mode 100644
index 0000000..156be6d
--- /dev/null
+++ b/jsonloader2v2/parse_creation_info.go
@@ -0,0 +1,122 @@
+package jsonloader2v2
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/spdx/tools-golang/spdx"
+)
+
+func (spec JSONSpdxDocument) parseJsonCreationInfo2_2(key string, value interface{}, doc *spdxDocument2_2) error {
+ // create an SPDX Creation Info data struct if we don't have one already
+
+ if doc.CreationInfo == nil {
+ doc.CreationInfo = &spdx.CreationInfo2_2{
+ ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
+ }
+ }
+ ci := doc.CreationInfo
+ switch key {
+ case "dataLicense":
+ ci.DataLicense = value.(string)
+ case "spdxVersion":
+ ci.SPDXVersion = value.(string)
+ case "SPDXID":
+ id, err := extractElementID(value.(string))
+ if err != nil {
+ return fmt.Errorf("%s", err)
+ }
+ ci.SPDXIdentifier = id
+ case "documentNamespace":
+ ci.DocumentNamespace = value.(string)
+ case "name":
+ ci.DocumentName = value.(string)
+ case "comment":
+ ci.DocumentComment = value.(string)
+ case "creationInfo":
+ creationInfo := value.(map[string]interface{})
+ for key, val := range creationInfo {
+ switch key {
+ case "comment":
+ ci.CreatorComment = val.(string)
+ case "created":
+ ci.Created = val.(string)
+ case "licenseListVersion":
+ ci.LicenseListVersion = val.(string)
+ case "creators":
+ err := parseCreators(creationInfo["creators"], ci)
+ if err != nil {
+ return fmt.Errorf("%s", err)
+ }
+ }
+ }
+ case "externalDocumentRefs":
+ err := parseExternalDocumentRefs(value, ci)
+ if err != nil {
+ return fmt.Errorf("%s", err)
+ }
+ default:
+ return fmt.Errorf("unrecognized key %v", key)
+
+ }
+
+ return nil
+}
+
+// ===== Helper functions =====
+
+func parseCreators(creators interface{}, ci *spdx.CreationInfo2_2) error {
+ if reflect.TypeOf(creators).Kind() == reflect.Slice {
+ s := reflect.ValueOf(creators)
+
+ for i := 0; i < s.Len(); i++ {
+ fmt.Println(s.Index(i))
+ subkey, subvalue, err := extractSubs(s.Index(i).Interface().(string))
+ if err != nil {
+ return err
+ }
+ switch subkey {
+ case "Person":
+ ci.CreatorPersons = append(ci.CreatorPersons, strings.TrimSuffix(subvalue, " ()"))
+ case "Organization":
+ ci.CreatorOrganizations = append(ci.CreatorOrganizations, strings.TrimSuffix(subvalue, " ()"))
+ case "Tool":
+ ci.CreatorTools = append(ci.CreatorTools, subvalue)
+ default:
+ return fmt.Errorf("unrecognized Creator type %v", subkey)
+ }
+
+ }
+ }
+ return nil
+}
+
+func parseExternalDocumentRefs(references interface{}, ci *spdx.CreationInfo2_2) error {
+ if reflect.TypeOf(references).Kind() == reflect.Slice {
+ s := reflect.ValueOf(references)
+
+ for i := 0; i < s.Len(); i++ {
+ fmt.Println(s.Index(i))
+ ref := s.Index(i).Interface().(map[string]interface{})
+ documentRefID := ref["externalDocumentId"].(string)
+ if !strings.HasPrefix(documentRefID, "DocumentRef-") {
+ return fmt.Errorf("expected first element to have DocumentRef- prefix")
+ }
+ documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-")
+ if documentRefID == "" {
+ return fmt.Errorf("document identifier has nothing after prefix")
+ }
+ checksum := ref["checksum"].(map[string]interface{})
+ edr := spdx.ExternalDocumentRef2_2{
+ DocumentRefID: documentRefID,
+ URI: ref["spdxDocument"].(string),
+ Alg: checksum["algorithm"].(string),
+ Checksum: checksum["checksumValue"].(string),
+ }
+
+ ci.ExternalDocumentReferences[documentRefID] = edr
+ }
+ }
+ return nil
+}
diff --git a/jsonloader2v2/parse_creation_info_test.go b/jsonloader2v2/parse_creation_info_test.go
new file mode 100644
index 0000000..3580902
--- /dev/null
+++ b/jsonloader2v2/parse_creation_info_test.go
@@ -0,0 +1,167 @@
+package jsonloader2v2
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/spdx/tools-golang/spdx"
+)
+
+func TestJSONSpdxDocument_parseJsonCreationInfo2_2(t *testing.T) {
+
+ var specs JSONSpdxDocument
+
+ type args struct {
+ key string
+ value interface{}
+ doc *spdxDocument2_2
+ }
+ tests := []struct {
+ name string
+ spec JSONSpdxDocument
+ args args
+ want *spdx.CreationInfo2_2
+ wantErr bool
+ }{
+ // TODO: Add test cases.
+ // check whether DataLicense is being parsed
+ {
+ name: "DataLicense",
+ spec: specs,
+ args: args{
+ key: "dataLicense",
+ value: "CC0-1.0",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{DataLicense: "CC0-1.0", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether SPDXID is being parsed
+ {
+ name: "SPDXID",
+ spec: specs,
+ args: args{
+ key: "SPDXID",
+ value: "SPDXRef-DOCUMENT",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{SPDXIdentifier: "DOCUMENT", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether DocumentName is being parsed
+ {
+ name: "DocumentName",
+ spec: specs,
+ args: args{
+ key: "name",
+ value: "SPDX-Tools-v2.0",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{DocumentName: "SPDX-Tools-v2.0", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether SPDXVersion is being parsed
+ {
+ name: "spdxVersion",
+ spec: specs,
+ args: args{
+ key: "spdxVersion",
+ value: "SPDX-2.2",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{SPDXVersion: "SPDX-2.2", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether DocumentComment is being parsed
+ {
+ name: "comment",
+ spec: specs,
+ args: args{
+ key: "comment",
+ value: "This document was created using SPDX 2.0 using licenses from the web site.",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{DocumentComment: "This document was created using SPDX 2.0 using licenses from the web site.", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether DocumentNamespace is being parsed
+ {
+ name: "documentNamespace",
+ spec: specs,
+ args: args{
+ key: "documentNamespace",
+ value: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301",
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{DocumentNamespace: "http://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301", ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{}},
+ wantErr: false,
+ },
+ // check whether CreationInfo(Creators , CreatorComment and Licence List Version) is being parsed
+ {
+ name: "creationInfo",
+ spec: specs,
+ args: args{
+ key: "creationInfo",
+ value: map[string]interface{}{
+ "comment": "This package has been shipped in source and binary form.\nThe binaries were created with gcc 4.5.1 and expect to link to\ncompatible system run time libraries.",
+ "created": "2010-01-29T18:30:22Z",
+ "creators": []string{"Tool: LicenseFind-1.0", "Organization: ExampleCodeInspect ()", "Person: Jane Doe ()"},
+ "licenseListVersion": "3.8",
+ },
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{
+ CreatorComment: "This package has been shipped in source and binary form.\nThe binaries were created with gcc 4.5.1 and expect to link to\ncompatible system run time libraries.",
+ Created: "2010-01-29T18:30:22Z",
+ CreatorPersons: []string{"Jane Doe"},
+ CreatorOrganizations: []string{"ExampleCodeInspect"},
+ CreatorTools: []string{"LicenseFind-1.0"},
+ LicenseListVersion: "3.8",
+ ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{},
+ },
+ wantErr: false,
+ },
+ // check whether ExternalDocumentReferences is being parsed
+ {
+ name: "externalDocumentRefs",
+ spec: specs,
+ args: args{
+ key: "externalDocumentRefs",
+ value: []map[string]interface{}{
+ {
+ "externalDocumentId": "DocumentRef-spdx-tool-1.2",
+ "spdxDocument": "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301",
+ "checksum": map[string]interface{}{
+ "algorithm": "SHA1",
+ "checksumValue": "d6a770ba38583ed4bb4525bd96e50461655d2759",
+ },
+ },
+ },
+ doc: &spdxDocument2_2{},
+ },
+ want: &spdx.CreationInfo2_2{
+ ExternalDocumentReferences: map[string]spdx.ExternalDocumentRef2_2{
+ "spdx-tool-1.2": {
+ DocumentRefID: "spdx-tool-1.2",
+ URI: "http://spdx.org/spdxdocs/spdx-tools-v1.2-3F2504E0-4F89-41D3-9A0C-0305E82C3301",
+ Alg: "SHA1",
+ Checksum: "d6a770ba38583ed4bb4525bd96e50461655d2759",
+ },
+ },
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.args.doc = &spdxDocument2_2{}
+ if err := tt.spec.parseJsonCreationInfo2_2(tt.args.key, tt.args.value, tt.args.doc); (err != nil) != tt.wantErr {
+ t.Errorf("JSONSpdxDocument.parseJsonCreationInfo2_2() error = %v, wantErr %v", err, tt.wantErr)
+ }
+ if !reflect.DeepEqual(tt.args.doc.CreationInfo, tt.want) {
+ t.Errorf("Load2_2() = %v, want %v", tt.args.doc.CreationInfo, tt.want)
+ }
+
+ })
+ }
+}
diff --git a/jsonloader2v2/types.go b/jsonloader2v2/types.go
new file mode 100644
index 0000000..61c3941
--- /dev/null
+++ b/jsonloader2v2/types.go
@@ -0,0 +1,7 @@
+package jsonloader2v2
+
+import "github.com/spdx/tools-golang/spdx"
+
+type spdxDocument2_2 spdx.Document2_2
+
+type JSONSpdxDocument map[string]interface{}
diff --git a/jsonloader2v2/util.go b/jsonloader2v2/util.go
new file mode 100644
index 0000000..eeafa90
--- /dev/null
+++ b/jsonloader2v2/util.go
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package jsonloader2v2
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/spdx/tools-golang/spdx"
+)
+
+//TODO: inspect all functions
+
+// used to extract key / value from embedded substrings
+// returns subkey, subvalue, nil if no error, or "", "", error otherwise
+func extractSubs(value string) (string, string, error) {
+ // parse the value to see if it's a valid subvalue format
+ sp := strings.SplitN(value, ":", 2)
+ if len(sp) == 1 {
+ return "", "", fmt.Errorf("invalid subvalue format for %s (no colon found)", value)
+ }
+
+ subkey := strings.TrimSpace(sp[0])
+ subvalue := strings.TrimSpace(sp[1])
+
+ return subkey, subvalue, nil
+}
+
+// used to extract DocumentRef and SPDXRef values from an SPDX Identifier
+// which can point either to this document or to a different one
+func extractDocElementID(value string) (spdx.DocElementID, error) {
+ docRefID := ""
+ idStr := value
+
+ // check prefix to see if it's a DocumentRef ID
+ if strings.HasPrefix(idStr, "DocumentRef-") {
+ // extract the part that comes between "DocumentRef-" and ":"
+ strs := strings.Split(idStr, ":")
+ // should be exactly two, part before and part after
+ if len(strs) < 2 {
+ return spdx.DocElementID{}, fmt.Errorf("no colon found although DocumentRef- prefix present")
+ }
+ if len(strs) > 2 {
+ return spdx.DocElementID{}, fmt.Errorf("more than one colon found")
+ }
+
+ // trim the prefix and confirm non-empty
+ docRefID = strings.TrimPrefix(strs[0], "DocumentRef-")
+ if docRefID == "" {
+ return spdx.DocElementID{}, fmt.Errorf("document identifier has nothing after prefix")
+ }
+ // and use remainder for element ID parsing
+ idStr = strs[1]
+ }
+
+ // check prefix to confirm it's got the right prefix for element IDs
+ if !strings.HasPrefix(idStr, "SPDXRef-") {
+ return spdx.DocElementID{}, fmt.Errorf("missing SPDXRef- prefix for element identifier")
+ }
+
+ // make sure no colons are present
+ if strings.Contains(idStr, ":") {
+ // we know this means there was no DocumentRef- prefix, because
+ // we would have handled multiple colons above if it was
+ return spdx.DocElementID{}, fmt.Errorf("invalid colon in element identifier")
+ }
+
+ // trim the prefix and confirm non-empty
+ eltRefID := strings.TrimPrefix(idStr, "SPDXRef-")
+ if eltRefID == "" {
+ return spdx.DocElementID{}, fmt.Errorf("element identifier has nothing after prefix")
+ }
+
+ // we're good
+ return spdx.DocElementID{DocumentRefID: docRefID, ElementRefID: spdx.ElementID(eltRefID)}, nil
+}
+
+// used to extract SPDXRef values only from an SPDX Identifier which can point
+// to this document only. Use extractDocElementID for parsing IDs that can
+// refer either to this document or a different one.
+func extractElementID(value string) (spdx.ElementID, error) {
+ // check prefix to confirm it's got the right prefix for element IDs
+ if !strings.HasPrefix(value, "SPDXRef-") {
+ return spdx.ElementID(""), fmt.Errorf("missing SPDXRef- prefix for element identifier")
+ }
+
+ // make sure no colons are present
+ if strings.Contains(value, ":") {
+ return spdx.ElementID(""), fmt.Errorf("invalid colon in element identifier")
+ }
+
+ // trim the prefix and confirm non-empty
+ eltRefID := strings.TrimPrefix(value, "SPDXRef-")
+ if eltRefID == "" {
+ return spdx.ElementID(""), fmt.Errorf("element identifier has nothing after prefix")
+ }
+
+ // we're good
+ return spdx.ElementID(eltRefID), nil
+}