From 03f6d8e51152b1862b2d999b79b90a088b06037f Mon Sep 17 00:00:00 2001 From: bnewbold Date: Tue, 17 Apr 2012 20:12:19 -0400 Subject: listing --- bommom.go | 67 ++++++++++++++++++++++++++++++++-- core.go | 17 +++++++++ core_test.go | 15 -------- store.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 180 insertions(+), 35 deletions(-) diff --git a/bommom.go b/bommom.go index 3b7c993..4b6e39d 100644 --- a/bommom.go +++ b/bommom.go @@ -41,13 +41,73 @@ func main() { switch flag.Arg(0) { default: log.Fatal("Error: unknown command: ", flag.Arg(0)) - case "load", "dump", "serve": + case "load", "serve": log.Fatal("Error: Unimplemented, sorry") case "init": log.Println("Initializing...") + initCmd() + case "dump": + log.Println("Dumping...") + dumpCmd() + case "list": + listCmd() } } +func initCmd() { + _, err := NewJSONFileBomStore(*fileStorePath) + if err != nil { + log.Fatal(err) + } +} + +func dumpCmd() { + b := makeTestBom() + b.Version = "v001" + bs := &BomStub{Name: "widget", + Owner: "common", + Description: "fancy stuff", + HeadVersion: b.Version, + IsPublicView: true, + IsPublicEdit: true} + jfbs, err := OpenJSONFileBomStore(*fileStorePath) + if err != nil { + log.Fatal(err) + } + jfbs.Persist(bs, b, "v001") +} + +func listCmd() { + jfbs, err := OpenJSONFileBomStore(*fileStorePath) + if err != nil { + log.Fatal(err) + } + var bomStubs []BomStub + if flag.NArg() > 2 { + log.Fatal("Error: too many arguments...") + } + if flag.NArg() == 2 { + name := flag.Arg(1) + if !isShortName(name) { + log.Fatal("Error: not a possible username: " + name) + } + bomStubs, err = jfbs.ListBoms(ShortName(name)) + if err != nil { + log.Fatal(err) + } + } else { + // list all boms from all names + // TODO: ERROR + bomStubs, err = jfbs.ListBoms("") + if err != nil { + log.Fatal(err) + } + } + for _, bs := range bomStubs { + fmt.Println(bs.Owner + "/" + bs.Name) + } +} + func printUsage() { fmt.Println("bommom is a tool for managing and publishing electronics BOMs") fmt.Println("") @@ -57,8 +117,9 @@ func printUsage() { fmt.Println("Commands:") fmt.Println("") fmt.Println("\tinit \t\t initialize BOM and authentication datastores") - fmt.Println("\tload [file]\t import a BOM") - fmt.Println("\tdump [name]\t dump a BOM to stdout") + fmt.Println("\tlist [user]\t\t list BOMs, optionally filtered by user") + fmt.Println("\tload \t import a BOM") + fmt.Println("\tdump \t dump a BOM to stdout") fmt.Println("\tserve\t\t serve up web interface over HTTP") fmt.Println("") fmt.Println("Extra command line options:") diff --git a/core.go b/core.go index 3b756d8..5d76f71 100644 --- a/core.go +++ b/core.go @@ -65,3 +65,20 @@ func (b *Bom) AddLineItem(li *LineItem) error { b.LineItems = append(b.LineItems, *li) return nil } + +// ---------- testing +func makeTestBom() *Bom { + op1 := OfferPrice{Currency: "usd", Price: 1.0, MinQty: 1} + op2 := OfferPrice{Currency: "usd", Price: 0.8, MinQty: 100} + o := Offer{Sku: "A123", Distributor: "Acme", Prices: []OfferPrice{op1, op2}} + //o.AddOfferPrice(op1) + //o.AddOfferPrice(op2) + li := LineItem{Mfg: "WidgetCo", + Mpn: "WIDG0001", + Elements: []string{"W1", "W2"}, + Offers: []Offer{o}} + //li.AddOffer(o) + b := NewBom("test01") + b.AddLineItem(&li) + return b +} diff --git a/core_test.go b/core_test.go index 7e6ae2d..700052f 100644 --- a/core_test.go +++ b/core_test.go @@ -7,21 +7,6 @@ import ( "testing" ) -func makeTestBom() *Bom { - op1 := OfferPrice{Currency: "usd", Price: 1.0, MinQty: 1} - op2 := OfferPrice{Currency: "usd", Price: 0.8, MinQty: 100} - o := Offer{Sku: "A123", Distributor: "Acme", Prices: []OfferPrice{op1, op2}} - //o.AddOfferPrice(op1) - //o.AddOfferPrice(op2) - li := LineItem{Mfg: "WidgetCo", - Mpn: "WIDG0001", - Elements: []string{"W1", "W2"}, - Offers: []Offer{o}} - //li.AddOffer(o) - b := NewBom("test01") - b.AddLineItem(&li) - return b -} func TestNewBom(t *testing.T) { b := makeTestBom() diff --git a/store.go b/store.go index 11b2b5c..105890c 100644 --- a/store.go +++ b/store.go @@ -4,6 +4,7 @@ import ( "encoding/json" "log" "os" + "path" ) var bomstore BomStore @@ -14,26 +15,35 @@ type BomStore interface { GetHead(user, name ShortName) (*Bom, error) GetBom(user, name, version ShortName) (*Bom, error) Persist(bs *BomStub, b *Bom, version ShortName) error + ListBoms(user ShortName) (*Bom, error) } // Basic BomStore backend using a directory structure of JSON files saved to // disk. type JSONFileBomStore struct { - RootPath string + Rootfpath string } -func NewJSONFileBomStore(path string) *JSONFileBomStore { - err := os.MkdirAll(path, os.ModePerm|os.ModeDir) +func NewJSONFileBomStore(fpath string) (*JSONFileBomStore, error) { + err := os.MkdirAll(fpath, os.ModePerm|os.ModeDir) if err != nil && !os.IsExist(err) { - log.Fatal(err) + return nil, err } - return &JSONFileBomStore{RootPath: path} + return &JSONFileBomStore{Rootfpath: fpath}, nil +} + +func OpenJSONFileBomStore(fpath string) (*JSONFileBomStore, error) { + _, err := os.Open(fpath) + if err != nil && !os.IsExist(err) { + return nil, err + } + return &JSONFileBomStore{Rootfpath: fpath}, nil } func (jfbs *JSONFileBomStore) GetStub(user, name ShortName) (*BomStub, error) { - path := jfbs.RootPath + "/" + string(user) + "/" + string(name) + "/meta.json" + fpath := jfbs.Rootfpath + "/" + string(user) + "/" + string(name) + "/_meta.json" bs := BomStub{} - if err := readJsonBomStub(path, &bs); err != nil { + if err := readJsonBomStub(fpath, &bs); err != nil { return nil, err } return &bs, nil @@ -52,20 +62,87 @@ func (jfbs *JSONFileBomStore) GetHead(user, name ShortName) (*Bom, error) { } func (jfbs *JSONFileBomStore) GetBom(user, name, version ShortName) (*Bom, error) { - path := jfbs.RootPath + "/" + string(user) + "/" + string(name) + "/" + string(version) + ".json" + fpath := jfbs.Rootfpath + "/" + string(user) + "/" + string(name) + "/" + string(version) + ".json" b := Bom{} - if err := readJsonBom(path, &b); err != nil { + if err := readJsonBom(fpath, &b); err != nil { return nil, err } return &b, nil } +func (jfbs *JSONFileBomStore) ListBoms(user ShortName) ([]BomStub, error) { + if user != "" { + return jfbs.listBomsForUser(user) + } + // else iterator over all users... + rootDir, err := os.Open(jfbs.Rootfpath) + if err != nil { + log.Fatal(err) + } + defer rootDir.Close() + bsList := []BomStub{} + dirInfo, err := rootDir.Readdir(0) + for _, node := range dirInfo { + if !node.IsDir() || !isShortName(node.Name()) { + continue + } + uList, err := jfbs.listBomsForUser(ShortName(node.Name())) + if err != nil { + log.Fatal(err) + } + bsList = append(bsList, uList...) + } + return bsList, nil +} + +func (jfbs *JSONFileBomStore) listBomsForUser(user ShortName) ([]BomStub, error) { + bsList := []BomStub{} + uDirPath:= jfbs.Rootfpath + "/" + string(user) + uDir, err := os.Open(uDirPath) + if err != nil { + if e, ok := err.(*os.PathError); ok && e.Err.Error() == "no such file or directory" { + // XXX: should probably check for a specific syscall error? same below + return bsList, nil + } + return nil, err + } + defer uDir.Close() + dirContents , err := uDir.Readdir(0) + if err != nil { + return nil, err + } + for _, node := range dirContents { + if !node.IsDir() || !isShortName(node.Name()) { + continue + } + fpath := jfbs.Rootfpath + "/" + string(user) + "/" + node.Name() + "/_meta.json" + bs := BomStub{} + if err := readJsonBomStub(fpath, &bs); err != nil { + if e, ok := err.(*os.PathError); ok && e.Err.Error() == "no such file or directory" { + // no _meta.json in there + continue + } + return nil, err + } + bsList = append(bsList, bs) + } + return bsList, nil +} + func (jfbs *JSONFileBomStore) Persist(bs *BomStub, b *Bom, version ShortName) error { + b_fpath := jfbs.Rootfpath + "/" + string(bs.Owner) + "/" + string(bs.Name) + "/" + string(version) + ".json" + bs_fpath := jfbs.Rootfpath + "/" + string(bs.Owner) + "/" + string(bs.Name) + "/_meta.json" + if err := writeJsonBomStub(bs_fpath, bs); err != nil { + log.Fatal(err) + } + if err := writeJsonBom(b_fpath, b); err != nil { + log.Fatal(err) + } return nil } -func readJsonBomStub(path string, bs *BomStub) error { - f, err := os.Open(path) +func readJsonBomStub(fpath string, bs *BomStub) error { + f, err := os.Open(path.Clean(fpath)) if err != nil { return err } @@ -77,8 +154,12 @@ func readJsonBomStub(path string, bs *BomStub) error { return nil } -func writeJsonBomStub(path string, bs *BomStub) error { - f, err := os.Create(path) +func writeJsonBomStub(fpath string, bs *BomStub) error { + err := os.MkdirAll(path.Dir(fpath), os.ModePerm|os.ModeDir) + if err != nil && !os.IsExist(err) { + return err + } + f, err := os.Create(fpath) if err != nil { return err } @@ -90,8 +171,8 @@ func writeJsonBomStub(path string, bs *BomStub) error { return nil } -func readJsonBom(path string, b *Bom) error { - f, err := os.Open(path) +func readJsonBom(fpath string, b *Bom) error { + f, err := os.Open(path.Clean(fpath)) if err != nil { return err } @@ -103,8 +184,9 @@ func readJsonBom(path string, b *Bom) error { return nil } -func writeJsonBom(path string, b *Bom) error { - f, err := os.Create(path) +// Need to write the BomStub before writing the Bom +func writeJsonBom(fpath string, b *Bom) error { + f, err := os.Create(fpath) if err != nil { return err } -- cgit v1.2.3