-
Notifications
You must be signed in to change notification settings - Fork 61
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
DO NOT MERGE: SPDX 3 Bindings #249
Conversation
I'd like to try and pair this down to the basics required to create an SPDX "document". So far, I've been operation on the assumption that this requires:
For example, in example1 of the spdx-examples repo, the bare minimum required json is:
I'm not sure if creating an SPDX document requires a package, but from my experimentation, it does require a CreationInfo and an Agent to be created beforehand. In order to create the CreationInfo, the Agent's SPDXID needs to be created first. |
Yes, that looks correct to me. |
Me too - that's about the minimum |
First pass feedback:
|
Some initial testing:
->
I'm actually surprised the compiler can find that the
|
I'll see if I can make something work. Is there a specific minimum version of go we should support? 1.13 feels very old as a minimum version. If there is a way to allow the "generic" types in 1.13 I'm fine with that but the bindings will be.... pretty cantankerous if there isn't a way to do this.
Something like: p := model.MakePerson()
p.Name().Set("Joshua")
email := model.MakeExternalIndentifier()
email.ExternalIdentifierType().Set(model.ExternalIdentifierTypeEmail)
email.Identifier().Set("[email protected]")
# I might add an append to add a single item to the end, because that is common
p.ExternalIdentifier().Set([]Ref[ExternalIdentifier]{model.MakeObjectRef(email)})
Can you be more specific? In general, you should only be dealing with interfaces.
That's really not possible if we want to do any sort of assignment validation. The assignment validation is really helpful in the other language bindings since it gives immediate feedback about where you're using the model wrong, so I think the bindings are much more useful with them, even if it's not "the go way". We really need to be dealing with all the SPDX objects via interfaces to keep the polymorphism as best possible (at least as far as I can tell), and you can't (AFAICT) have properties on interfaces, just getters and setters. |
The way to do this is just not use
Oh I see my problem: in some places interfaces are called "Element", "Agent" etc, and in others, it's called "PropertyInterface" rather than "Property". But now I know where to look for things. Maybe consistent naming for the SHACL parts and the SPDX parts?
I got it - manipulating objects happen at the SHACL level so that validation works. Let me try again. |
Hmm, I don't think that's the same. I'm using Generics here: https://go.dev/doc/tutorial/generics which it looks like were added in go 1.18.... it would be pretty annoying to write this code without them TBH (it would have to be way more verbose)
|
Can you finish this with CreationInfo? I am having trouble with the |
ci := model.MakeCreationInfo()
ci.SpecVersion().Set("3.0.1")
ci.CreatedBy().Set(model.MakeObjectRef(p))
ci.Created().Set(time.Now())
p.CreationInfo().Set(model.MakeObjectRef(ci)) |
Oh, right, the typing is a little weird with the references in golang.... hang on and I'll get an example that works. |
8881f20
to
827b6ab
Compare
@nishakm Hey, you found a bug :) ! The latest version works with this example code: package main
import (
"time"
"os"
"fmt"
"encoding/json"
"model"
)
func main() {
// Note: Object de-duplication when serializing is not implemented in the
// go bindings yet, so this example code links by IRI reference instead of
// by object reference. This would not normally be the recommended way to
// do this, as object references are much more usable
p := v3_0_1.MakePerson()
p.ID().Set("http://spdx.org/spdxdocs/person-example")
p.Name().Set("Joshua")
ci := v3_0_1.MakeCreationInfo()
// This would not normally be required, but references are done by IRI
// because deduplication is not implemented
ci.ID().Set("_:creation-info")
ci.SpecVersion().Set("3.0.1")
// Normally would do:
// ci.CreatedBy().Set([]v3_0_1.Ref[v3_0_1.Agent]{v3_0_1.MakeObjectRef[v3_0_1.Agent](p)})
ci.CreatedBy().Set([]v3_0_1.Ref[v3_0_1.Agent]{v3_0_1.MakeIRIRef[v3_0_1.Agent](p.ID().Get())})
ci.Created().Set(time.Now())
// Normally would do:
//p.CreationInfo().Set(v3_0_1.MakeObjectRef(ci))
p.CreationInfo().Set(v3_0_1.MakeIRIRef[v3_0_1.CreationInfo](ci.ID().Get()))
email := v3_0_1.MakeExternalIdentifier()
email.ExternalIdentifierType().Set(v3_0_1.ExternalIdentifierTypeEmail)
email.Identifier().Set("[email protected]")
p.ExternalIdentifier().Set([]v3_0_1.Ref[v3_0_1.ExternalIdentifier]{v3_0_1.MakeObjectRef(email)})
doc := v3_0_1.MakeSpdxDocument()
doc.ID().Set("http://spdx.org/spdxdocs/doc-example")
// Normally would do:
//doc.CreationInfo().Set(v3_0_1.MakeObjectRef(ci))
doc.CreationInfo().Set(v3_0_1.MakeIRIRef[v3_0_1.CreationInfo](ci.ID().Get()))
objset := v3_0_1.NewSHACLObjectSet()
objset.AddObject(p)
objset.AddObject(doc)
// The creation info would not normally be added manually, but it has to be
// since it's referenced by IRI (not by object) due to no deduplication
// when serializing
objset.AddObject(ci)
file, err := os.Create("out.json")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
encoder := json.NewEncoder(file)
if err := objset.Encode(encoder); err != nil {
fmt.Println(err)
os.Exit(1)
}
} Note that serialization is still very incomplete in the go bindings, so this code does references by IRI instead of by object, but the places that affects are commented in the code |
This is a *very* initial pass at the generated go bindings from shacl2code. These bindings are incomplete, but give a basic idea of what they might look like for evaluation purposes
827b6ab
to
662b62b
Compare
OK, I was motivated to do some cleanup here. The object references are now fixed, as well as few other things. Here is the new test code which works and passes SPDX 3 validation: package main
import (
"time"
"os"
"fmt"
"encoding/json"
"model"
)
func main() {
objset := v3_0_1.NewSHACLObjectSet()
p := v3_0_1.MakePerson()
p.ID().Set("http://spdx.org/spdxdocs/person-example")
p.Name().Set("Joshua")
objset.AddObject(p)
ci := v3_0_1.MakeCreationInfo()
ci.SpecVersion().Set("3.0.1")
ci.CreatedBy().Append(v3_0_1.MakeObjectRef[v3_0_1.Agent](p))
ci.Created().Set(time.Now().UTC())
p.CreationInfo().Set(v3_0_1.MakeObjectRef(ci))
email := v3_0_1.MakeExternalIdentifier()
email.ExternalIdentifierType().Set(v3_0_1.ExternalIdentifierTypeEmail)
email.Identifier().Set("[email protected]")
p.ExternalIdentifier().Append(v3_0_1.MakeObjectRef(email))
doc := v3_0_1.MakeSpdxDocument()
doc.ID().Set("http://spdx.org/spdxdocs/doc-example")
doc.CreationInfo().Set(v3_0_1.MakeObjectRef(ci))
objset.AddObject(doc)
file, err := os.Create("out.json")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer file.Close()
encoder := json.NewEncoder(file)
if err := objset.Encode(encoder); err != nil {
fmt.Println(err)
os.Exit(1)
}
} |
@JPEWdev So you can use
But this is really annoying so I agree with you that we probably need to move to the latest Go version (1.23 at this time). We may need to host a new repo in spdx for this specifically (at least for now). |
This looks right to me...
|
@JPEWdev it looks like |
My code right now:
|
The code bindings are going to be put in spdx/spdx-go-model#1 so further discussion should be moved there |
This is a very initial pass at the generated go bindings from shacl2code. These bindings are incomplete, but give a basic idea of what they might look like for evaluation purposes