Skip to content

Commit

Permalink
feat: implement Store method
Browse files Browse the repository at this point in the history
Signed-off-by: Jonathan Howard <jonathan.w.howard@lmco.com>
  • Loading branch information
jhoward-lm committed Apr 26, 2024
1 parent db188d0 commit 74e5efc
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 0 deletions.
17 changes: 17 additions & 0 deletions backends/ent/retrieve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// --------------------------------------------------------------
// SPDX-FileCopyrightText: Copyright © 2024 The Protobom Authors
// SPDX-FileType: SOURCE
// SPDX-License-Identifier: Apache-2.0
// --------------------------------------------------------------
package ent

import (
"github.com/bom-squad/protobom/pkg/sbom"

"github.com/protobom/storage/pkg/options"
)

// Retrieve implements the model.v1.storage.Backend interface

Check failure on line 14 in backends/ent/retrieve.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
func (EntBackend) Retrieve(string, *options.RetrieveOptions) (*sbom.Document, error) {
return nil, nil
}
246 changes: 246 additions & 0 deletions backends/ent/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// --------------------------------------------------------------
// SPDX-FileCopyrightText: Copyright © 2024 The Protobom Authors
// SPDX-FileType: SOURCE
// SPDX-License-Identifier: Apache-2.0
// --------------------------------------------------------------
package ent

import (
"context"
"database/sql"
"errors"
"fmt"
"slices"

"github.com/bom-squad/protobom/pkg/sbom"
sqlite "github.com/glebarez/go-sqlite"

"github.com/protobom/storage/internal/backends/ent"
"github.com/protobom/storage/internal/backends/ent/documenttype"
ent_node "github.com/protobom/storage/internal/backends/ent/node"
"github.com/protobom/storage/pkg/options"
)

// Enable SQLite foreign key support.
const dsnParams string = "?_pragma=foreign_keys(1)"

type (
// EntBackend implements the protobom model.v1.storage.Backend interface.
EntBackend struct{}

Check warning on line 29 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

exported: type name will be used as ent.EntBackend by other packages, and that stutters; consider calling this Backend (revive)

// EntOptions contains options specific to the protobom ent backend.
EntOptions struct {

Check warning on line 32 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

exported: type name will be used as ent.EntOptions by other packages, and that stutters; consider calling this Options (revive)
DatabaseFile string
}
)

var errInvalidEntOptions = errors.New("invalid ent backend options")

// NewEntOptions returns an EntOptions with optional file path for database file.
//
// If not provided, the database file will default to ":memory:" for a temporary, in-memory database.
func NewEntOptions(file string) EntOptions {
if file == "" {
file = ":memory:"
}

return EntOptions{DatabaseFile: file}
}

// Store implements the model.v1.storage.Backend interface

Check failure on line 50 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
func (EntBackend) Store(bom *sbom.Document, opts *options.StoreOptions) error {

Check failure on line 51 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

calculated cyclomatic complexity for function Store is 11, max is 10 (cyclop)
var err error

if opts == nil {
opts = &options.StoreOptions{}
}

if opts.BackendOptions == nil {
opts.BackendOptions = NewEntOptions("")
}

entOpts, ok := opts.BackendOptions.(EntOptions)
if !ok {
return fmt.Errorf("%w: %v", errInvalidEntOptions, opts.BackendOptions)
}

// Register the SQLite driver as "sqlite3".
if !slices.Contains(sql.Drivers(), "sqlite3") {
sqlite.RegisterAsSQLITE3()
}

client, err := ent.Open("sqlite3", fmt.Sprintf("%s%s", entOpts.DatabaseFile, dsnParams))
if err != nil {
return fmt.Errorf("failed opening connection to sqlite: %w", err)
}

defer client.Close()

tx, err := client.Tx(context.Background())
if err != nil {
return fmt.Errorf("failed to create transactional client: %w", err)
}

ctx := ent.NewTxContext(context.Background(), tx)

// Run the auto migration tool.
if err := client.Schema.Create(ctx); err != nil {
return fmt.Errorf("failed creating schema resources: %w", err)
}

documentCreate := tx.Document.Create().
SetMetadata(metadataToEnt(ctx, tx, bom.Metadata)).
SetNodeList(nodeListToEnt(ctx, tx, bom.NodeList))

_, err = documentCreate.Save(ctx)
if err != nil && errors.Is(err, sql.ErrNoRows) {
return fmt.Errorf("failed to save document: %w", err)
}

if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit database transaction: %w", err)
}

return nil
}

func documentTypesToEnt(ctx context.Context, tx *ent.Tx, docTypes []*sbom.DocumentType) ent.DocumentTypes {
entDocTypes := ent.DocumentTypes{}

for _, docType := range docTypes {
typeName := documenttype.Type(*docType.Type)
docTypeCreate := tx.DocumentType.Create().
SetNillableName(docType.Name).
SetNillableDescription(docType.Description).
SetNillableType(&typeName)

entDocType, err := docTypeCreate.Save(ctx)
if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

entDocTypes = append(entDocTypes, entDocType)
}

return entDocTypes
}

func metadataToEnt(ctx context.Context, tx *ent.Tx, md *sbom.Metadata) *ent.Metadata {
metadataCreate := tx.Metadata.Create().
SetID(md.Id).
SetVersion(md.Version).
SetName(md.Name).
SetDate(md.Date.AsTime()).
SetComment(md.Comment)

metadata, err := metadataCreate.
AddAuthors(personsToEnt(ctx, tx, md.Authors)...).
AddDocumentTypes(documentTypesToEnt(ctx, tx, md.DocumentTypes)...).
AddTools(toolsToEnt(ctx, tx, md.Tools)...).
Save(ctx)

if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

return metadata
}

func nodeListToEnt(ctx context.Context, tx *ent.Tx, nodeList *sbom.NodeList) *ent.NodeList {
nodeListCreate := tx.NodeList.Create().SetRootElements(nodeList.RootElements)

entNodeList, err := nodeListCreate.AddNodes(nodesToEnt(ctx, tx, nodeList.Nodes)...).Save(ctx)
if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

return entNodeList
}

func nodesToEnt(ctx context.Context, tx *ent.Tx, nodes []*sbom.Node) ent.Nodes {
var entNodes ent.Nodes
for _, node := range nodes {

Check failure on line 162 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

ranges should only be cuddled with assignments used in the iteration (wsl)
nodeCreate := tx.Node.Create().
SetID(node.Id).
SetAttribution(node.Attribution).
SetBuildDate(node.BuildDate.AsTime()).
SetComment(node.Comment).
SetCopyright(node.Copyright).
SetDescription(node.Description).
SetFileName(node.FileName).
SetFileTypes(node.FileTypes).
SetLicenseComments(node.LicenseComments).
SetLicenseConcluded(node.LicenseConcluded).
SetLicenses(node.Licenses).
SetName(node.Name).
SetReleaseDate(node.ReleaseDate.AsTime()).
SetSourceInfo(node.SourceInfo).
SetSummary(node.Summary).
SetType(ent_node.Type(node.Type.String())).
SetURLDownload(node.UrlDownload).
SetURLHome(node.UrlHome).
SetValidUntilDate(node.ValidUntilDate.AsTime()).
SetVersion(node.Version)

nodeCreate.
AddEdgeTypes().
AddExternalReferences().
AddHashes().
AddIdentifiers().
AddNodes().
AddOriginators().
AddPrimaryPurpose().
AddSuppliers()

entNode, err := nodeCreate.Save(ctx)

if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

entNodes = append(entNodes, entNode)
}

return entNodes
}

func personsToEnt(ctx context.Context, tx *ent.Tx, persons []*sbom.Person) ent.Persons {
var entPersons ent.Persons
for _, person := range persons {

Check failure on line 209 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

ranges should only be cuddled with assignments used in the iteration (wsl)
entPerson, err := tx.Person.Create().
SetName(person.Name).
SetEmail(person.Email).
SetIsOrg(person.IsOrg).
SetPhone(person.Phone).
SetURL(person.Url).
AddContacts(personsToEnt(ctx, tx, person.Contacts)...).
Save(ctx)

if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

entPersons = append(entPersons, entPerson)
}

return entPersons
}

func toolsToEnt(ctx context.Context, tx *ent.Tx, tools []*sbom.Tool) ent.Tools {
var entTools ent.Tools
for _, tool := range tools {

Check failure on line 231 in backends/ent/store.go

View workflow job for this annotation

GitHub Actions / lint

ranges should only be cuddled with assignments used in the iteration (wsl)
entTool, err := tx.Tool.Create().
SetName(tool.Name).
SetVendor(tool.Vendor).
SetVersion(tool.Version).
Save(ctx)

if err != nil && errors.Is(err, sql.ErrNoRows) {
panic(err)
}

entTools = append(entTools, entTool)
}

return entTools
}
9 changes: 9 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,30 @@ go 1.22.0
require (
entgo.io/ent v0.13.1
github.com/bom-squad/protobom v0.3.0
github.com/glebarez/go-sqlite v1.22.0
)

require (
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect
github.com/CycloneDX/cyclonedx-go v0.8.0 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/hashicorp/hcl/v2 v2.13.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/spdx/tools-golang v0.5.3 // indirect
github.com/zclconf/go-cty v1.8.0 // indirect
golang.org/x/mod v0.15.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
modernc.org/libc v1.37.6 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
modernc.org/sqlite v1.28.0 // indirect
)
19 changes: 19 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1l
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
Expand All @@ -28,6 +32,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgCNc=
Expand All @@ -40,12 +46,16 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
Expand Down Expand Up @@ -78,6 +88,7 @@ golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -97,4 +108,12 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
7 changes: 7 additions & 0 deletions internal/backends/ent/migrate/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions internal/backends/ent/schema/node_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"entgo.io/ent/schema"
"entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field"
"entgo.io/ent/schema/index"
)

type NodeList struct {
Expand All @@ -29,4 +30,10 @@ func (NodeList) Edges() []ent.Edge {
}
}

func (NodeList) Indexes() []ent.Index {
return []ent.Index{
index.Fields("root_elements").Unique(),
}
}

func (NodeList) Annotations() []schema.Annotation { return nil }

0 comments on commit 74e5efc

Please sign in to comment.