diff options
author | specter25 <ujjwalcoding012@gmail.com> | 2021-06-12 18:22:28 +0530 |
---|---|---|
committer | specter25 <ujjwalcoding012@gmail.com> | 2021-06-12 18:22:28 +0530 |
commit | 0a0e69fc25d9b22b756fcd3c26725d85935b8697 (patch) | |
tree | 92238de2d67dd51fc51cce9b1c12eb9ca62d3bf3 | |
parent | 8e09d22f514a2eeee9a3044fb47d0cb6dbc74a6a (diff) | |
download | spdx-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.go | 68 | ||||
-rw-r--r-- | jsonloader2v2/parse_creation_info.go | 122 | ||||
-rw-r--r-- | jsonloader2v2/parse_creation_info_test.go | 167 | ||||
-rw-r--r-- | jsonloader2v2/types.go | 7 | ||||
-rw-r--r-- | jsonloader2v2/util.go | 100 |
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 +} |