diff --git a/Makefile b/Makefile index 18153a4..2914d61 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,9 @@ clean: rm -rf release/ rm -rf cert/ +lint: + GO111MODULE=on go run utils/ci.go lint + travis: GO111MODULE=on go run utils/ci.go install GO111MODULE=on go run utils/ci.go test -coverage $$TEST_PACKAGES diff --git a/godbledger/cmd/config.go b/godbledger/cmd/config.go index 3b7581b..da83813 100644 --- a/godbledger/cmd/config.go +++ b/godbledger/cmd/config.go @@ -73,8 +73,10 @@ func MakeConfig(cli *cli.Context) (error, *LedgerConfig) { return err, nil } logrus.SetLevel(level) - if len(cli.String("config")) > 0 { - config.ConfigFile = cli.String("config") + setConfig(cli, config) + if config.DataDirectory != DefaultDataDir() { + config.ConfigFile = config.DataDirectory + "/config.toml" + config.DatabaseLocation = config.DataDirectory + "/ledgerdata/ledger.db" } err = InitConfig(config) if err != nil { diff --git a/godbledger/cmd/flags.go b/godbledger/cmd/flags.go index 77af2c7..4027c68 100644 --- a/godbledger/cmd/flags.go +++ b/godbledger/cmd/flags.go @@ -109,7 +109,7 @@ func setConfig(ctx *cli.Context, cfg *LedgerConfig) { cfg.ConfigFile = ctx.String(ConfigFileFlag.Name) } if ctx.IsSet(DataDirFlag.Name) { - cfg.ConfigFile = ctx.String(DataDirFlag.Name) + cfg.DataDirectory = ctx.String(DataDirFlag.Name) } if ctx.IsSet(RPCHost.Name) { cfg.Host = ctx.String(RPCHost.Name) diff --git a/godbledger/core/transactions_test.go b/godbledger/core/transactions_test.go new file mode 100644 index 0000000..2c9c606 --- /dev/null +++ b/godbledger/core/transactions_test.go @@ -0,0 +1,65 @@ +package core + +import ( + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func init() { +} + +func TestTransaction(t *testing.T) { + user, err := NewUser("Tester") + assert.NoError(t, err) + + txn, err := NewTransaction(user) + assert.NoError(t, err) + + cash, err := NewAccount("1", "cash") + assert.NoError(t, err) + income, err := NewAccount("2", "income") + assert.NoError(t, err) + aud, err := NewCurrency("AUD", 2) + assert.NoError(t, err) + + amountDR := big.NewInt(10) + + spl1, err := NewSplit(time.Now(), []byte("Cash Income"), []*Account{cash}, aud, amountDR) + assert.NoError(t, err) + + err = txn.AppendSplit(spl1) + assert.NoError(t, err) + + amountCR := big.NewInt(-10) + spl2, err := NewSplit(time.Now(), []byte("Cash Income"), []*Account{income}, aud, amountCR) + assert.NoError(t, err) + + err = txn.AppendSplit(spl2) + assert.NoError(t, err) + + total, txnBalances := txn.Balance() + assert.True(t, txnBalances) + assert.Equal(t, total.Cmp(big.NewInt(0)), 0) + + assert.Equal(t, txn.Splits[0].Amount, amountDR) + assert.Equal(t, txn.Splits[0].Accounts[0].Name, "cash") + assert.Equal(t, txn.Splits[1].Amount, amountCR) + assert.Equal(t, txn.Splits[1].Accounts[0].Name, "income") + + //func ReverseTransaction(originalTxn *Transaction, usr *User) (*Transaction, error) { + reversedTxn, err := ReverseTransaction(txn, user) + assert.NoError(t, err) + + total, txnBalances = reversedTxn.Balance() + assert.True(t, txnBalances) + assert.Equal(t, total.Cmp(big.NewInt(0)), 0) + + assert.Equal(t, reversedTxn.Splits[0].Amount, amountCR) + assert.Equal(t, reversedTxn.Splits[0].Accounts[0].Name, "cash") + assert.Equal(t, reversedTxn.Splits[1].Amount, amountDR) + assert.Equal(t, reversedTxn.Splits[1].Accounts[0].Name, "income") + +} diff --git a/godbledger/ledger/ledger.go b/godbledger/ledger/ledger.go index 16009d5..5573440 100644 --- a/godbledger/ledger/ledger.go +++ b/godbledger/ledger/ledger.go @@ -42,6 +42,7 @@ func New(ctx *cli.Context, cfg *cmd.LedgerConfig) (*Ledger, error) { } log.WithField("path", dbPath).Debug("Checking db path") if ctx.Bool(cmd.ClearDB.Name) { + log.Info("Clearing SQLite3 DB") if err := sqlite3db.ClearDB(dbPath); err != nil { return nil, err } diff --git a/godbledger/main.go b/godbledger/main.go index 816de71..53d8f64 100644 --- a/godbledger/main.go +++ b/godbledger/main.go @@ -68,12 +68,13 @@ func main() { } app.Version = version.Version app.Commands = []*cli.Command{ - // See config.go + // See cmd/config.go cmd.DumpConfigCommand, cmd.InitConfigCommand, } app.Flags = []cli.Flag{ + // See cmd/flags.go cmd.VerbosityFlag, cmd.DataDirFlag, cmd.ClearDB, diff --git a/godbledger/node/node.go b/godbledger/node/node.go index 25387f1..cd21463 100644 --- a/godbledger/node/node.go +++ b/godbledger/node/node.go @@ -7,7 +7,6 @@ import ( "syscall" "github.com/sirupsen/logrus" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" "github.com/darcys22/godbledger/godbledger/core" diff --git a/godbledger/node/node_test.go b/godbledger/node/node_test.go new file mode 100644 index 0000000..7311316 --- /dev/null +++ b/godbledger/node/node_test.go @@ -0,0 +1,90 @@ +package node + +import ( + "context" + "crypto/rand" + "flag" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/darcys22/godbledger/godbledger/cmd" + "github.com/darcys22/godbledger/godbledger/ledger" + "github.com/darcys22/godbledger/shared" + + "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" + "github.com/urfave/cli/v2" +) + +const ( + maxPollingWaitTime = 1 * time.Second +) + +func init() { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetOutput(ioutil.Discard) +} + +// Test that godbledger node can close. +func TestNodeClose_OK(t *testing.T) { + hook := logTest.NewGlobal() + + app := cli.App{} + set := flag.NewFlagSet("test", 0) + set.String("config", "", "doc") + + ctx := cli.NewContext(&app, set, nil) + + node, err := New(ctx) + assert.NoError(t, err) + + node.Close() + + shared.LogsContain(t.Fatalf, hook, "Stopping ledger node", true) +} + +// TestClearDB tests clearing the database +func TestClearDB(t *testing.T) { + hook := logTest.NewGlobal() + + randPath, err := rand.Int(rand.Reader, big.NewInt(1000000)) + assert.NoError(t, err, "Could not generate random number for file path") + tmp := filepath.Join(os.TempDir(), fmt.Sprintf("datadirtest%d", randPath)) + assert.NoError(t, os.RemoveAll(tmp)) + + app := cli.App{} + set := flag.NewFlagSet("test", 0) + set.Bool(cmd.ClearDB.Name, true, "") + + ctx := cli.NewContext(&app, set, nil) + assert.NoError(t, err) + err, cfg := cmd.MakeConfig(ctx) + assert.NoError(t, err) + cfg.DatabaseType = "memorydb" + cfg.DataDirectory = tmp + + node, err := New(ctx) + assert.NoError(t, err) + + ledger, err := ledger.New(ctx, cfg) + assert.NoError(t, err) + + node.Register(ledger) + go node.Start() + d := time.Now().Add(maxPollingWaitTime) + contextWithDeadline, cancel := context.WithDeadline(context.Background(), d) + defer cancel() + <-contextWithDeadline.Done() + shared.LogsContain(t.Fatalf, hook, "Clearing SQLite3 DB", true) + //case <-contextWithDeadline.Done(): + + node.Close() + assert.NoError(t, os.RemoveAll(tmp)) +} diff --git a/godbledger/rpc/service_test.go b/godbledger/rpc/service_test.go new file mode 100644 index 0000000..21cb315 --- /dev/null +++ b/godbledger/rpc/service_test.go @@ -0,0 +1,91 @@ +package rpc + +import ( + "context" + "errors" + "flag" + "io/ioutil" + "testing" + + "github.com/darcys22/godbledger/godbledger/cmd" + "github.com/darcys22/godbledger/godbledger/ledger" + "github.com/darcys22/godbledger/shared" + + "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "github.com/urfave/cli/v2" +) + +func init() { + logrus.SetLevel(logrus.DebugLevel) + logrus.SetOutput(ioutil.Discard) +} + +func TestLifecycle_OK(t *testing.T) { + hook := logTest.NewGlobal() + + set := flag.NewFlagSet("test", 0) + set.String("config", "", "doc") + ctx := cli.NewContext(nil, set, nil) + err, cfg := cmd.MakeConfig(ctx) + assert.NoError(t, err) + + cfg.DatabaseType = "memorydb" + cfg.Host = "127.0.0.1" + cfg.RPCPort = "7348" + cfg.CACert = "bob.crt" + cfg.Cert = "alice.crt" + cfg.Key = "alice.key" + + ledger, err := ledger.New(ctx, cfg) + assert.NoError(t, err) + + rpcService := NewRPCService(context.Background(), &Config{ + Host: cfg.Host, + Port: cfg.RPCPort, + CACertFlag: cfg.CACert, + CertFlag: cfg.Cert, + KeyFlag: cfg.Key, + }, ledger) + + rpcService.Start() + + shared.LogsContain(t.Fatalf, hook, "GRPC Listening on port", true) + assert.NoError(t, rpcService.Stop()) +} + +func TestStatus_CredentialError(t *testing.T) { + credentialErr := errors.New("credentialError") + s := &Service{credentialError: credentialErr} + + assert.Contains(t, s.credentialError.Error(), s.Status().Error()) +} + +func TestRPC_InsecureEndpoint(t *testing.T) { + hook := logTest.NewGlobal() + + set := flag.NewFlagSet("test", 0) + set.String("config", "", "doc") + ctx := cli.NewContext(nil, set, nil) + err, cfg := cmd.MakeConfig(ctx) + assert.NoError(t, err) + + cfg.DatabaseType = "memorydb" + cfg.Host = "127.0.0.1" + cfg.RPCPort = "7777" + + ledger, err := ledger.New(ctx, cfg) + assert.NoError(t, err) + + rpcService := NewRPCService(context.Background(), &Config{ + Host: cfg.Host, + Port: cfg.RPCPort, + }, ledger) + + rpcService.Start() + + shared.LogsContain(t.Fatalf, hook, "GRPC Listening on port", true) + shared.LogsContain(t.Fatalf, hook, "You are using an insecure gRPC server", true) + assert.NoError(t, rpcService.Stop()) +} diff --git a/godbledger/version/version.go b/godbledger/version/version.go index 66528ae..b6a6a31 100644 --- a/godbledger/version/version.go +++ b/godbledger/version/version.go @@ -22,7 +22,7 @@ import ( const ( VersionMajor = 0 // Major version component of the current release - VersionMinor = 4 // Minor version component of the current release + VersionMinor = 5 // Minor version component of the current release VersionPatch = 0 // Patch version component of the current release VersionMeta = "alpha" // Version metadata to append to the version string ) diff --git a/ledger_cli/addcurrency.go b/ledger_cli/addcurrency.go index c3f7868..ee5c2ab 100644 --- a/ledger_cli/addcurrency.go +++ b/ledger_cli/addcurrency.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "strconv" "time" @@ -11,7 +12,6 @@ import ( pb "github.com/darcys22/godbledger/proto" "google.golang.org/grpc" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" ) @@ -36,7 +36,7 @@ var commandAddCurrency = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } if ctx.NArg() > 0 { @@ -46,7 +46,7 @@ var commandAddCurrency = &cli.Command{ log.WithField("address", address).Info("GRPC Dialing on port") conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { - log.Fatalf("did not connect: %v", err) + return fmt.Errorf("Could not connect to GRPC (%v)", err) } defer conn.Close() client := pb.NewTransactorClient(conn) @@ -56,48 +56,46 @@ var commandAddCurrency = &cli.Command{ if ctx.Bool("delete") { req := &pb.DeleteCurrencyRequest{ - Currency: ctx.Args().Get(0), - Signature: "blah", + Currency: ctx.Args().Get(0), } r, err := client.DeleteCurrency(ctxtimeout, req) if err != nil { - log.Fatalf("could not greet: %v", err) + return fmt.Errorf("Could not call Delete Currency Method (%v)", err) } - log.Printf("Delete Currency Response: %s", r.GetMessage()) + log.Infof("Delete Currency Response: %s", r.GetMessage()) } else { if ctx.NArg() > 1 { decimals, err := strconv.ParseInt(ctx.Args().Get(1), 0, 64) if err != nil { - log.Fatalf("could not parse the decimals provided: %v", err) + return fmt.Errorf("Could not parse the decimals provided (%v)", err) } req := &pb.CurrencyRequest{ - Currency: ctx.Args().Get(0), - Decimals: decimals, - Signature: "blah", + Currency: ctx.Args().Get(0), + Decimals: decimals, } r, err := client.AddCurrency(ctxtimeout, req) if err != nil { - log.Fatalf("could not create currency: %v", err) + return fmt.Errorf("Could not call Add Currency Method (%v)", err) } - log.Printf("Create Currency Response: %s", r.GetMessage()) + log.Infof("Create Currency Response: %s", r.GetMessage()) } else { - log.Printf("This command two arguments.") + return errors.New("This command requires two arguments") } } if err != nil { - log.Fatalf("Failed with Error: %v", err) + return fmt.Errorf("Failed with Error (%v)", err) } } else { - log.Printf("This command requires an argument.") + return errors.New("This command requires an argument") } return nil diff --git a/ledger_cli/delete.go b/ledger_cli/delete.go index 34766e6..396e4bf 100644 --- a/ledger_cli/delete.go +++ b/ledger_cli/delete.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "time" @@ -10,7 +11,6 @@ import ( pb "github.com/darcys22/godbledger/proto" "google.golang.org/grpc" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" ) @@ -25,7 +25,7 @@ var commandDeleteTransaction = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } if ctx.NArg() > 0 { @@ -36,7 +36,7 @@ var commandDeleteTransaction = &cli.Command{ if cfg.CACert != "" && cfg.Cert != "" && cfg.Key != "" { tlsCredentials, err := loadTLSCredentials(cfg) if err != nil { - log.Fatal("cannot load TLS credentials: ", err) + return fmt.Errorf("Could not load TLS credentials (%v)", err) } opts = append(opts, grpc.WithTransportCredentials(tlsCredentials)) } else { @@ -46,7 +46,7 @@ var commandDeleteTransaction = &cli.Command{ // Set up a connection to the server. conn, err := grpc.Dial(address, opts...) if err != nil { - log.Fatalf("did not connect: %v", err) + return fmt.Errorf("Could not connect to GRPC (%v)", err) } defer conn.Close() client := pb.NewTransactorClient(conn) @@ -54,19 +54,16 @@ var commandDeleteTransaction = &cli.Command{ ctxtimeout, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - signature := "test" req := &pb.DeleteRequest{ Identifier: ctx.Args().Get(0), - Signature: signature, } r, err := client.DeleteTransaction(ctxtimeout, req) if err != nil { - log.Fatalf("could not delete: %v", err) + return fmt.Errorf("Could not call Delete Transaction Method (%v)", err) } - log.Printf("Response: %s", r.GetMessage()) - return nil + log.Infof("Delete Transaction Response: %s", r.GetMessage()) } else { - log.Fatalf("This command requires an argument.") + return errors.New("This command requires an argument") } return nil @@ -84,7 +81,7 @@ var commandVoidTransaction = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } if ctx.NArg() > 0 { @@ -95,7 +92,7 @@ var commandVoidTransaction = &cli.Command{ if cfg.CACert != "" && cfg.Cert != "" && cfg.Key != "" { tlsCredentials, err := loadTLSCredentials(cfg) if err != nil { - log.Fatal("cannot load TLS credentials: ", err) + return fmt.Errorf("Could not load TLS credentials (%v)", err) } opts = append(opts, grpc.WithTransportCredentials(tlsCredentials)) } else { @@ -105,7 +102,7 @@ var commandVoidTransaction = &cli.Command{ // Set up a connection to the server. conn, err := grpc.Dial(address, opts...) if err != nil { - log.Fatalf("did not connect: %v", err) + return fmt.Errorf("Could not connect to GRPC (%v)", err) } defer conn.Close() client := pb.NewTransactorClient(conn) @@ -113,19 +110,16 @@ var commandVoidTransaction = &cli.Command{ ctxtimeout, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - signature := "test" req := &pb.DeleteRequest{ Identifier: ctx.Args().Get(0), - Signature: signature, } r, err := client.VoidTransaction(ctxtimeout, req) if err != nil { - log.Fatalf("could not void: %v", err) + return fmt.Errorf("Could not call Void Transaction Method (%v)", err) } - log.Printf("Response: %s", r.GetMessage()) - return nil + log.Infof("Void Transaction Response: %s", r.GetMessage()) } else { - log.Fatalf("This command requires an argument.") + return errors.New("This command requires an argument") } return nil diff --git a/ledger_cli/file.go b/ledger_cli/file.go index 91b7090..6ec2e28 100644 --- a/ledger_cli/file.go +++ b/ledger_cli/file.go @@ -16,6 +16,7 @@ package main import ( //"flag" + "errors" "fmt" "strings" "unicode/utf8" @@ -41,7 +42,7 @@ var commandFile = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } var ledgerFileName string @@ -56,20 +57,18 @@ var commandFile = &cli.Command{ ledgerFileReader, err := NewLedgerReader(ledgerFileName) if err != nil { - log.Printf("error reading file, %v\n", err) - return err + return fmt.Errorf("Could not read file %s (%v)", ledgerFileName, err) } generalLedger, parseError := ParseLedger(ledgerFileReader) if parseError != nil { - log.Printf("error parsing file, %s\n", parseError.Error()) - return parseError + return fmt.Errorf("Could not parse file (%v)", parseError) } PrintLedger(generalLedger, columnWidth) SendLedger(cfg, generalLedger) } else { - log.Printf("This command requires an argument.") + return errors.New("This command requires an argument") } return nil }, diff --git a/ledger_cli/jsonjournal.go b/ledger_cli/jsonjournal.go index d4cd80e..c1bee73 100644 --- a/ledger_cli/jsonjournal.go +++ b/ledger_cli/jsonjournal.go @@ -18,14 +18,14 @@ var commandJSONJournal = &cli.Command{ Example - ledger_cli jsonjournal '{"Payee":"ijfjie","Date":"2019-06-30T00:00:00Z","AccountChanges":[{"Name":"Cash","Description":"jisfeij","Currency":"USD","Balance":"100"},{"Name":"Income","Description":"another","Currency":"USD","Balance":"-100"}],"Signature":"stuff"}' + ledger_cli jsonjournal '{"Payee":"ijfjie","Date":"2019-06-30T00:00:00Z","AccountChanges":[{"Name":"Cash","Description":"jisfeij","Currency":"USD","Balance":"100"},{"Name":"Income","Description":"another","Currency":"USD","Balance":"-100"}]}' `, Flags: []cli.Flag{}, Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } // we initialize our request struct @@ -35,11 +35,11 @@ var commandJSONJournal = &cli.Command{ // jsonFile's content into 'users' which we defined above json.Unmarshal([]byte(ctx.Args().Get(0)), &req) - fmt.Printf("%v\n", req) + log.Debugf("Transaction: %v\n", req) err = Send(cfg, &req) if err != nil { - log.Fatalf("could not send: %v", err) + return fmt.Errorf("Could not send transaction (%v)", err) } return nil diff --git a/ledger_cli/tagaccount.go b/ledger_cli/tagaccount.go index 174aed6..79a271a 100644 --- a/ledger_cli/tagaccount.go +++ b/ledger_cli/tagaccount.go @@ -30,7 +30,7 @@ var commandTagAccount = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } address := fmt.Sprintf("%s:%s", cfg.Host, cfg.RPCPort) @@ -40,7 +40,7 @@ var commandTagAccount = &cli.Command{ if cfg.CACert != "" && cfg.Cert != "" && cfg.Key != "" { tlsCredentials, err := loadTLSCredentials(cfg) if err != nil { - log.Fatal("cannot load TLS credentials: ", err) + return fmt.Errorf("Could not load TLS credentials (%v)", err) } opts = append(opts, grpc.WithTransportCredentials(tlsCredentials)) } else { @@ -48,10 +48,9 @@ var commandTagAccount = &cli.Command{ } // Set up a connection to the server. - //conn, err := grpc.Dial(address, grpc.WithInsecure()) conn, err := grpc.Dial(address, opts...) if err != nil { - log.Fatalf("did not connect: %v", err) + return fmt.Errorf("Could not connect to GRPC (%v)", err) } defer conn.Close() client := pb.NewTransactorClient(conn) @@ -61,34 +60,28 @@ var commandTagAccount = &cli.Command{ if ctx.Bool("delete") { req := &pb.DeleteTagRequest{ - Account: ctx.Args().Get(0), - Tag: ctx.Args().Get(1), - Signature: "blah", + Account: ctx.Args().Get(0), + Tag: ctx.Args().Get(1), } r, err := client.DeleteTag(ctxtimeout, req) if err != nil { - log.Fatalf("could not greet: %v", err) + return fmt.Errorf("Could not call Delete Tag Method (%v)", err) } - log.Printf("Delete Tag Response: %s", r.GetMessage()) + log.Infof("Delete Tag Response: %s", r.GetMessage()) } else { req := &pb.TagRequest{ - Account: ctx.Args().Get(0), - Tag: ctx.Args().Get(1), - Signature: "blah", + Account: ctx.Args().Get(0), + Tag: ctx.Args().Get(1), } r, err := client.AddTag(ctxtimeout, req) if err != nil { - log.Fatalf("could not greet: %v", err) + return fmt.Errorf("Could not call Add Tag Method (%v)", err) } - log.Printf("Create Tag Response: %s", r.GetMessage()) - } - - if err != nil { - log.Fatalf("could not greet: %v", err) + log.Infof("Create Tag Response: %s", r.GetMessage()) } return nil diff --git a/ledger_cli/transaction.go b/ledger_cli/transaction.go index 6f2cdec..b8c71a4 100644 --- a/ledger_cli/transaction.go +++ b/ledger_cli/transaction.go @@ -28,7 +28,7 @@ var commandSingleTestTransaction = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } date, _ := time.Parse("2006-01-02", "2011-03-15") @@ -62,12 +62,11 @@ var commandSingleTestTransaction = &cli.Command{ Date: date, Payee: desc, AccountChanges: transactionLines, - Signature: "stuff", } err = Send(cfg, req) if err != nil { - log.Fatalf("could not send: %v", err) + return fmt.Errorf("Could not send transaction (%v)", err) } return nil @@ -83,7 +82,7 @@ func Send(cfg *cmd.LedgerConfig, t *Transaction) error { if cfg.CACert != "" && cfg.Cert != "" && cfg.Key != "" { tlsCredentials, err := loadTLSCredentials(cfg) if err != nil { - log.Fatal("cannot load TLS credentials: ", err) + return fmt.Errorf("Could not load TLS credentials (%v)", err) } opts = append(opts, grpc.WithTransportCredentials(tlsCredentials)) } else { @@ -94,7 +93,7 @@ func Send(cfg *cmd.LedgerConfig, t *Transaction) error { //conn, err := grpc.Dial(address, grpc.WithInsecure()) conn, err := grpc.Dial(address, opts...) if err != nil { - log.Fatalf("did not connect: %v", err) + return fmt.Errorf("Could not connect to GRPC (%v)", err) } defer conn.Close() client := pb.NewTransactorClient(conn) @@ -105,10 +104,11 @@ func Send(cfg *cmd.LedgerConfig, t *Transaction) error { transactionLines := make([]*pb.LineItem, 2) for i, accChange := range t.AccountChanges { + amountInt64 := accChange.Balance.Num().Int64() * int64(100) / accChange.Balance.Denom().Int64() transactionLines[i] = &pb.LineItem{ Accountname: accChange.Name, Description: accChange.Description, - Amount: accChange.Balance.Num().Int64(), + Amount: amountInt64, Currency: accChange.Currency, } } @@ -117,13 +117,12 @@ func Send(cfg *cmd.LedgerConfig, t *Transaction) error { Date: t.Date.Format("2006-01-02"), Description: t.Payee, Lines: transactionLines, - Signature: t.Signature, } r, err := client.AddTransaction(ctx, req) if err != nil { - log.Fatalf("Could not send transaction: %v", err) + return fmt.Errorf("Could not call Add Transaction Method (%v)", err) } - log.Printf("Response: %s", r.GetMessage()) + log.Infof("Add Transaction Response: %s", r.GetMessage()) return nil } func loadTLSCredentials(cfg *cmd.LedgerConfig) (credentials.TransportCredentials, error) { diff --git a/ledger_cli/types.go b/ledger_cli/types.go index b94e9d2..9d1c438 100644 --- a/ledger_cli/types.go +++ b/ledger_cli/types.go @@ -46,7 +46,6 @@ type Transaction struct { Payee string Date time.Time AccountChanges []Account - Signature string } type sortTransactions []*Transaction diff --git a/ledger_cli/wizard.go b/ledger_cli/wizard.go index 4deb382..d521d8c 100644 --- a/ledger_cli/wizard.go +++ b/ledger_cli/wizard.go @@ -11,7 +11,7 @@ import ( "github.com/darcys22/godbledger/godbledger/cmd" - //"github.com/urfave/cli" + "github.com/marcmak/calc/calc" "github.com/urfave/cli/v2" ) @@ -25,7 +25,7 @@ var commandWizardJournal = &cli.Command{ Action: func(ctx *cli.Context) error { err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } reader := bufio.NewReader(os.Stdin) @@ -37,7 +37,7 @@ var commandWizardJournal = &cli.Command{ datetext, _ := reader.ReadString('\n') date, err := time.Parse("2006-01-02", strings.TrimSpace(datetext)) if err != nil { - panic(err) + return fmt.Errorf("Could not make parse date string %s with error (%v)", datetext, err) } fmt.Print("Enter the Journal Descripion: ") @@ -58,12 +58,10 @@ var commandWizardJournal = &cli.Command{ lineAccount, _ := reader.ReadString('\n') fmt.Print("Enter the Amount: ") - var i int64 - _, err := fmt.Scanf("%d", &i) - if err != nil { - panic(err) - } - lineAmount := big.NewRat(i, 1) + lineAmountStr, _ := reader.ReadString('\n') + + lineAmount := new(big.Rat) + lineAmount.SetFloat64(calc.Solve(lineAmountStr)) transactionLines = append(transactionLines, Account{ Name: lineAccount, @@ -85,18 +83,17 @@ var commandWizardJournal = &cli.Command{ Date: date, Payee: desc, AccountChanges: transactionLines, - Signature: "stuff", } bytes, err := json.Marshal(req) if err != nil { - fmt.Println("Can't serialize", req) + return fmt.Errorf("Can't Serialize Transaction (%v)", err) } - fmt.Printf("%v => %v, '%v'\n", req, bytes, string(bytes)) + log.Debugf("Transaction: %v => %v, '%v'\n", req, bytes, string(bytes)) err = Send(cfg, req) if err != nil { - log.Fatalf("could not send: %v", err) + return fmt.Errorf("Could not send transaction (%v)", err) } return nil diff --git a/proto/transaction.pb.go b/proto/transaction.pb.go index 06a9b2a..3d4e7a2 100644 --- a/proto/transaction.pb.go +++ b/proto/transaction.pb.go @@ -146,7 +146,6 @@ type TransactionRequest struct { Date string `protobuf:"bytes,1,opt,name=date,proto3" json:"date,omitempty"` Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` Lines []*LineItem `protobuf:"bytes,3,rep,name=lines,proto3" json:"lines,omitempty"` - Signature string `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -198,16 +197,8 @@ func (m *TransactionRequest) GetLines() []*LineItem { return nil } -func (m *TransactionRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type DeleteRequest struct { Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` - Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -245,13 +236,6 @@ func (m *DeleteRequest) GetIdentifier() string { return "" } -func (m *DeleteRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type TransactionResponse struct { Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -294,7 +278,6 @@ func (m *TransactionResponse) GetMessage() string { type TagRequest struct { Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` Tag string `protobuf:"bytes,2,opt,name=tag,proto3" json:"tag,omitempty"` - Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -339,17 +322,9 @@ func (m *TagRequest) GetTag() string { return "" } -func (m *TagRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type DeleteTagRequest struct { Account string `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"` Tag string `protobuf:"bytes,2,opt,name=tag,proto3" json:"tag,omitempty"` - Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -394,17 +369,9 @@ func (m *DeleteTagRequest) GetTag() string { return "" } -func (m *DeleteTagRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type CurrencyRequest struct { Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` Decimals int64 `protobuf:"varint,2,opt,name=decimals,proto3" json:"decimals,omitempty"` - Signature string `protobuf:"bytes,3,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -449,16 +416,8 @@ func (m *CurrencyRequest) GetDecimals() int64 { return 0 } -func (m *CurrencyRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type DeleteCurrencyRequest struct { Currency string `protobuf:"bytes,1,opt,name=currency,proto3" json:"currency,omitempty"` - Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -496,13 +455,6 @@ func (m *DeleteCurrencyRequest) GetCurrency() string { return "" } -func (m *DeleteCurrencyRequest) GetSignature() string { - if m != nil { - return m.Signature - } - return "" -} - type TBLine struct { Accountname string `protobuf:"bytes,1,opt,name=accountname,proto3" json:"accountname,omitempty"` Tags []string `protobuf:"bytes,2,rep,name=tags,proto3" json:"tags,omitempty"` @@ -846,48 +798,47 @@ func init() { func init() { proto.RegisterFile("transaction.proto", fileDescriptor_2cc4e03d2c28c490) } var fileDescriptor_2cc4e03d2c28c490 = []byte{ - // 654 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4f, 0x6f, 0xd3, 0x4e, - 0x10, 0xad, 0xeb, 0xc4, 0x4d, 0xc6, 0xbf, 0x26, 0xed, 0x56, 0x6d, 0xad, 0xfc, 0x0a, 0x8d, 0xf6, - 0x14, 0xa8, 0x54, 0xa4, 0x72, 0x40, 0x42, 0x5c, 0x92, 0x82, 0x0a, 0xa8, 0xb4, 0xaa, 0x1b, 0x22, - 0x0e, 0x5c, 0x16, 0x7b, 0xb1, 0x56, 0x4a, 0xec, 0xb0, 0xbb, 0x39, 0x70, 0xe5, 0x3b, 0x70, 0xe6, - 0x0b, 0xf0, 0x21, 0x91, 0xff, 0xac, 0xbb, 0xeb, 0x26, 0x69, 0x8a, 0x80, 0x9b, 0x77, 0x3c, 0xfb, - 0xde, 0x9b, 0xf1, 0xbc, 0x91, 0x61, 0x5b, 0x72, 0x12, 0x0b, 0x12, 0x48, 0x96, 0xc4, 0xc7, 0x53, - 0x9e, 0xc8, 0x04, 0xb9, 0x5a, 0x08, 0x7f, 0xb3, 0xa0, 0x71, 0xce, 0x62, 0xfa, 0x46, 0xd2, 0x09, - 0xea, 0x82, 0x4b, 0x82, 0x20, 0x99, 0xc5, 0x32, 0x26, 0x13, 0xea, 0x59, 0x5d, 0xab, 0xd7, 0xf4, - 0xf5, 0x50, 0x9a, 0x11, 0x52, 0x11, 0x70, 0x36, 0x4d, 0x6f, 0x7b, 0xeb, 0x79, 0x86, 0x16, 0x42, - 0x1d, 0x68, 0x04, 0x33, 0xce, 0x69, 0x1c, 0x7c, 0xf5, 0xec, 0xec, 0x75, 0x79, 0x46, 0x7b, 0xe0, - 0x90, 0x49, 0x8a, 0xe5, 0xd5, 0xba, 0x56, 0xcf, 0xf6, 0x8b, 0x13, 0x9e, 0x82, 0x3b, 0xbc, 0xd1, - 0x84, 0x10, 0xd4, 0x42, 0x22, 0x15, 0x7f, 0xf6, 0xbc, 0x02, 0xf1, 0x11, 0xd4, 0xc7, 0x2c, 0xa6, - 0xc2, 0xb3, 0xbb, 0x76, 0xcf, 0x3d, 0xd9, 0x3d, 0xd6, 0x2b, 0x57, 0x25, 0xfa, 0x79, 0x0e, 0xfe, - 0x6e, 0x01, 0xd2, 0x28, 0x7d, 0xfa, 0x65, 0x46, 0x85, 0xfc, 0x07, 0xcc, 0xe8, 0x00, 0x9a, 0x82, - 0x45, 0x31, 0x91, 0x33, 0x4e, 0xb3, 0x36, 0x34, 0xfd, 0x9b, 0x00, 0x7e, 0x07, 0x9b, 0x2f, 0xe9, - 0x98, 0x4a, 0xaa, 0x14, 0x3d, 0x04, 0x60, 0x21, 0x8d, 0x25, 0xfb, 0xcc, 0x28, 0x2f, 0x74, 0x69, - 0x11, 0x13, 0x6e, 0xbd, 0x0a, 0xf7, 0x04, 0x76, 0x8c, 0x2a, 0xc5, 0x34, 0x89, 0x05, 0x45, 0x1e, - 0x6c, 0x4c, 0xa8, 0x10, 0x24, 0x52, 0x95, 0xaa, 0x23, 0x1e, 0x01, 0x0c, 0x49, 0xa4, 0xc8, 0x3d, - 0xd8, 0x28, 0x3e, 0xbe, 0xca, 0x2b, 0x8e, 0x68, 0x0b, 0x6c, 0x49, 0xa2, 0x82, 0x30, 0x7d, 0x34, - 0x85, 0xd8, 0x55, 0x21, 0x1f, 0x61, 0x2b, 0xaf, 0xeb, 0xaf, 0xa0, 0x47, 0xd0, 0x3e, 0x2d, 0x66, - 0x4c, 0x81, 0xeb, 0x63, 0x68, 0x55, 0xc6, 0xb0, 0x03, 0x8d, 0x90, 0x06, 0x6c, 0x42, 0xc6, 0x22, - 0xe3, 0xb0, 0xfd, 0xf2, 0x7c, 0x07, 0xd1, 0x15, 0xec, 0xe6, 0x65, 0xdc, 0x87, 0x6e, 0xf9, 0x27, - 0xfa, 0x69, 0x81, 0x33, 0x1c, 0xa4, 0x53, 0xb2, 0x82, 0xfd, 0x10, 0xd4, 0x24, 0x89, 0x52, 0xd5, - 0x76, 0x3a, 0x9f, 0xe9, 0xb3, 0x66, 0x2a, 0x5b, 0x37, 0x95, 0x21, 0xa9, 0xb6, 0xa4, 0x03, 0xf5, - 0xdb, 0x1d, 0xc8, 0x11, 0xae, 0x25, 0xf7, 0x9c, 0x5c, 0x6e, 0x19, 0xc0, 0x87, 0xd0, 0x1c, 0x0e, - 0x96, 0xd8, 0x05, 0xf7, 0x61, 0xd3, 0xa7, 0xd3, 0x84, 0xcb, 0x65, 0x9e, 0x4a, 0x5b, 0x22, 0x09, - 0x97, 0xd9, 0x0b, 0xd5, 0x12, 0x15, 0xc0, 0xcf, 0x00, 0x52, 0x8e, 0x62, 0x58, 0x1f, 0x29, 0x77, - 0x59, 0x99, 0xbb, 0x76, 0x0c, 0x77, 0xe5, 0x9d, 0x53, 0xae, 0xbe, 0x84, 0xf6, 0x39, 0x13, 0x92, - 0xc5, 0x51, 0x79, 0xfb, 0x05, 0xfc, 0xa7, 0xe5, 0x2b, 0x10, 0xcf, 0x04, 0xd1, 0x2c, 0x62, 0x64, - 0xe3, 0xc7, 0xd0, 0x1a, 0x51, 0x2e, 0xb4, 0x0d, 0xb1, 0xd8, 0x3a, 0x47, 0xd0, 0x2e, 0x73, 0xef, - 0xf2, 0xd9, 0xc9, 0x0f, 0x07, 0x40, 0xd1, 0x26, 0x1c, 0xbd, 0x05, 0xf7, 0x22, 0x09, 0x69, 0x71, - 0x1f, 0xfd, 0x6f, 0xc8, 0x33, 0x15, 0x74, 0x0e, 0xe6, 0xbf, 0xcc, 0x29, 0xf1, 0x1a, 0x7a, 0x0f, - 0xad, 0x7e, 0x18, 0xea, 0xfb, 0xf4, 0x70, 0x61, 0xb5, 0x05, 0x64, 0x77, 0x71, 0x42, 0x09, 0x7b, - 0x0d, 0xdb, 0x85, 0x83, 0x35, 0xe4, 0x8e, 0x71, 0xd1, 0xd8, 0x5c, 0x2b, 0x81, 0x5e, 0x41, 0x7b, - 0x94, 0xb0, 0xf0, 0x4f, 0x42, 0xbe, 0x02, 0x27, 0x2d, 0x9f, 0x44, 0x68, 0xdf, 0xcc, 0x2e, 0x17, - 0xcf, 0x4a, 0x30, 0x17, 0xd0, 0x2c, 0x17, 0x16, 0x7a, 0x30, 0x47, 0xd3, 0x3d, 0xf1, 0x2e, 0xc1, - 0xed, 0x87, 0xe1, 0x69, 0xb9, 0x13, 0x8c, 0x2b, 0x95, 0x6d, 0xb2, 0x12, 0xe0, 0x07, 0x68, 0x99, - 0xab, 0x08, 0xe1, 0x39, 0x2a, 0x7f, 0x07, 0xf9, 0x35, 0xc0, 0x19, 0x95, 0x85, 0x91, 0x2a, 0xdf, - 0xc3, 0xb0, 0x76, 0x65, 0x14, 0x2b, 0xd6, 0xc3, 0x6b, 0xe8, 0x39, 0xd4, 0xcf, 0xa8, 0x1c, 0x0e, - 0xd0, 0x5e, 0xc5, 0xb4, 0x0a, 0x60, 0xff, 0x56, 0x5c, 0xdd, 0xfd, 0xe4, 0x64, 0x3f, 0x2b, 0x4f, - 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xdb, 0xdf, 0xc0, 0x62, 0xc1, 0x08, 0x00, 0x00, + // 625 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x55, 0x5f, 0x4f, 0xd4, 0x4e, + 0x14, 0xa5, 0xbf, 0x2e, 0x85, 0x3d, 0xfd, 0xc1, 0xc2, 0x10, 0xa0, 0x59, 0x51, 0x36, 0xf3, 0x84, + 0x92, 0x40, 0x02, 0x0f, 0x1a, 0x63, 0x4c, 0x40, 0x0d, 0x62, 0x08, 0xc4, 0xb2, 0x12, 0x5f, 0xc7, + 0x76, 0x6c, 0x9a, 0xb0, 0xed, 0xda, 0x99, 0x8d, 0xf1, 0xd5, 0x2f, 0xe2, 0x17, 0xf0, 0x43, 0x9a, + 0xfe, 0x99, 0xee, 0x4c, 0xdd, 0x5d, 0x17, 0x62, 0x7c, 0x9b, 0xb9, 0x73, 0xef, 0x39, 0xf7, 0xf6, + 0x9e, 0x7b, 0x8b, 0x75, 0x99, 0xb1, 0x44, 0xb0, 0x40, 0xc6, 0x69, 0x72, 0x30, 0xcc, 0x52, 0x99, + 0x12, 0x57, 0x33, 0xd1, 0xef, 0x16, 0x96, 0x2f, 0xe2, 0x84, 0x9f, 0x4b, 0x3e, 0x20, 0x3d, 0xb8, + 0x2c, 0x08, 0xd2, 0x51, 0x22, 0x13, 0x36, 0xe0, 0x9e, 0xd5, 0xb3, 0xf6, 0xda, 0xbe, 0x6e, 0xca, + 0x3d, 0x42, 0x2e, 0x82, 0x2c, 0x1e, 0xe6, 0xd1, 0xde, 0x7f, 0xa5, 0x87, 0x66, 0x22, 0x5d, 0x2c, + 0x07, 0xa3, 0x2c, 0xe3, 0x49, 0xf0, 0xcd, 0xb3, 0x8b, 0xe7, 0xfa, 0x4e, 0xb6, 0xe0, 0xb0, 0x41, + 0x8e, 0xe5, 0xb5, 0x7a, 0xd6, 0x9e, 0xed, 0x57, 0x37, 0x3a, 0x84, 0xdb, 0x1f, 0xe7, 0x44, 0x08, + 0x5a, 0x21, 0x93, 0x8a, 0xbf, 0x38, 0xcf, 0x41, 0xbc, 0x8f, 0xc5, 0xdb, 0x38, 0xe1, 0xc2, 0xb3, + 0x7b, 0xf6, 0x9e, 0x7b, 0xb4, 0x79, 0xa0, 0x57, 0xae, 0x4a, 0xf4, 0x4b, 0x1f, 0xfa, 0x15, 0x44, + 0x63, 0xf4, 0xf9, 0x97, 0x11, 0x17, 0xf2, 0x5f, 0x10, 0x1f, 0x62, 0xe5, 0x35, 0xbf, 0xe5, 0x92, + 0x2b, 0xce, 0x47, 0x40, 0x1c, 0xf2, 0x44, 0xc6, 0x9f, 0x63, 0x9e, 0x55, 0xcc, 0x9a, 0x85, 0x1e, + 0x62, 0xc3, 0xc8, 0x54, 0x0c, 0xd3, 0x44, 0x70, 0xe2, 0x61, 0x69, 0xc0, 0x85, 0x60, 0x91, 0xca, + 0x56, 0x5d, 0xe9, 0x33, 0xa0, 0xcf, 0x22, 0x05, 0xef, 0x61, 0xa9, 0xea, 0x9f, 0xf2, 0xab, 0xae, + 0x64, 0x0d, 0xb6, 0x64, 0x51, 0x55, 0x50, 0x7e, 0xa4, 0x2f, 0xb1, 0x56, 0xe6, 0x76, 0xcf, 0xf8, + 0x73, 0x74, 0x5e, 0x55, 0xad, 0x56, 0xe1, 0xba, 0x1a, 0xac, 0x86, 0x1a, 0xba, 0x58, 0x0e, 0x79, + 0x10, 0x0f, 0xd8, 0xad, 0x28, 0x50, 0x6c, 0xbf, 0xbe, 0xd3, 0x63, 0x6c, 0x96, 0xa9, 0xdc, 0x01, + 0x90, 0xfe, 0xb4, 0xe0, 0xf4, 0x4f, 0xf3, 0x2f, 0x3e, 0x87, 0x92, 0x09, 0x5a, 0x92, 0x45, 0x39, + 0xb3, 0x9d, 0xf7, 0x3a, 0x3f, 0x6b, 0xfa, 0xb4, 0x75, 0x7d, 0x1a, 0xa4, 0xad, 0x19, 0x55, 0x2c, + 0x9a, 0x55, 0x90, 0x1d, 0xb4, 0x4b, 0x84, 0x6b, 0x99, 0x79, 0x4e, 0x11, 0x38, 0x36, 0xd0, 0x5d, + 0xb4, 0xfb, 0xa7, 0x33, 0xa4, 0x47, 0x4f, 0xb0, 0xe2, 0xf3, 0x61, 0x9a, 0xc9, 0x59, 0xfa, 0xdc, + 0x41, 0x5b, 0x48, 0x96, 0xc9, 0xe2, 0xa1, 0x6c, 0xc6, 0xd8, 0x40, 0x9f, 0x02, 0x39, 0x47, 0x25, + 0x9a, 0xc7, 0x4a, 0xa9, 0x56, 0xa1, 0xd4, 0x0d, 0x43, 0xa9, 0xe5, 0x97, 0x53, 0x3a, 0xbd, 0x42, + 0xe7, 0x22, 0x16, 0x32, 0x4e, 0xa2, 0x3a, 0xfa, 0x05, 0xfe, 0xd7, 0xfc, 0x15, 0x88, 0x67, 0x82, + 0x68, 0x52, 0x35, 0xbc, 0xe9, 0x13, 0xac, 0xde, 0xf0, 0x4c, 0x68, 0xd3, 0x36, 0x5d, 0xc2, 0xfb, + 0xe8, 0xd4, 0xbe, 0x7f, 0xd2, 0xfb, 0xd1, 0x0f, 0x07, 0x50, 0xb4, 0x69, 0x46, 0xde, 0xc1, 0xbd, + 0x4c, 0x43, 0x5e, 0xc5, 0x93, 0x07, 0x46, 0x7a, 0x66, 0x06, 0xdd, 0x9d, 0xc9, 0x8f, 0x25, 0x25, + 0x5d, 0x20, 0x1f, 0xb0, 0x7a, 0x12, 0x86, 0xfa, 0x6a, 0xda, 0x9d, 0x5a, 0x6d, 0x05, 0xd9, 0x9b, + 0xee, 0x50, 0xc3, 0x5e, 0x63, 0xbd, 0x9a, 0x33, 0x0d, 0xb9, 0x6b, 0x04, 0x1a, 0x3b, 0x62, 0x2e, + 0xd0, 0xf7, 0xe8, 0xdc, 0xa4, 0x71, 0xf8, 0x37, 0x21, 0xdf, 0xc0, 0xc9, 0xcb, 0x67, 0x11, 0xd9, + 0x36, 0xbd, 0xeb, 0xf5, 0x30, 0x17, 0xcc, 0x25, 0xda, 0xf5, 0x5a, 0x21, 0x0f, 0x27, 0xe4, 0x74, + 0x47, 0xbc, 0x2b, 0xb8, 0x27, 0x61, 0xa8, 0x16, 0x03, 0x31, 0x9b, 0xd8, 0xd8, 0x17, 0x73, 0x01, + 0x7e, 0xc4, 0xaa, 0xb9, 0x6c, 0x08, 0x9d, 0x90, 0xe5, 0x7d, 0x90, 0xdf, 0x02, 0x67, 0x5c, 0x56, + 0x83, 0xd4, 0xe8, 0x87, 0x31, 0xda, 0x0d, 0x29, 0x36, 0x46, 0x8f, 0x2e, 0x90, 0xe7, 0x58, 0x3c, + 0xe3, 0xb2, 0x7f, 0x4a, 0xb6, 0x1a, 0x43, 0xab, 0x00, 0xb6, 0x7f, 0xb3, 0xab, 0xd8, 0x4f, 0x4e, + 0xf1, 0xdf, 0x3f, 0xfe, 0x15, 0x00, 0x00, 0xff, 0xff, 0xd4, 0xa9, 0x1d, 0x50, 0x0c, 0x08, 0x00, + 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/proto/transaction.proto b/proto/transaction.proto index c37048e..06c5f69 100644 --- a/proto/transaction.proto +++ b/proto/transaction.proto @@ -32,12 +32,10 @@ message TransactionRequest { string date = 1; string description = 2; repeated LineItem lines = 3; - string signature = 4; } message DeleteRequest { string identifier = 1; - string signature = 2; } message TransactionResponse { @@ -47,24 +45,20 @@ message TransactionResponse { message TagRequest { string account = 1; string tag = 2; - string signature = 3; } message DeleteTagRequest { string account = 1; string tag = 2; - string signature = 3; } message CurrencyRequest { string currency = 1; int64 decimals = 2; - string signature = 3; } message DeleteCurrencyRequest { string currency = 1; - string signature = 2; } message TBLine { diff --git a/reporter/console.go b/reporter/console.go index 74cd0b7..ff58742 100644 --- a/reporter/console.go +++ b/reporter/console.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/mattn/go-colorable" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" ) diff --git a/reporter/pdfgenerator.go b/reporter/pdfgenerator.go index 4d9595f..11a2fd3 100644 --- a/reporter/pdfgenerator.go +++ b/reporter/pdfgenerator.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io" "io/ioutil" "net/http" @@ -57,11 +58,11 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a //Check if keyfile path given and make sure it doesn't already exist. err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } ledger, err := ledger.New(ctx, cfg) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make new ledger (%v)", err) } queryDB := ` @@ -88,7 +89,7 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a log.Debugf("Quering the Database") rows, err := ledger.LedgerDb.Query(queryDB) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not query database (%v)", err) } defer rows.Close() accounts := make(map[string][]PDFAccount) @@ -98,7 +99,7 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a var t PDFAccount var name string if err := rows.Scan(&name, &t.Account, &t.Amount); err != nil { - log.Fatal(err) + return fmt.Errorf("Could not scan rows of query (%v)", err) } log.Debugf("%v", t) if val, ok := accounts[name]; ok { @@ -110,7 +111,7 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a } } if rows.Err() != nil { - log.Fatal(err) + return fmt.Errorf("rows errored with (%v)", rows.Err()) } for k, v := range accounts { @@ -122,18 +123,18 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a if _, err := os.Stat(dir); os.IsNotExist(err) { err = os.MkdirAll(dir, 0755) if err != nil { - panic(err) + return fmt.Errorf("Making Directory %s failed (%v)", dir, err) } } outputJson, _ := json.Marshal(reporteroutput) err = ioutil.WriteFile("src/output.json", outputJson, 0644) if err != nil { - panic(err) + return fmt.Errorf("writing output.json failed (%v)", err) } if err := DownloadFile("./src/pdfgenerator.js", "https://raw.githubusercontent.com/darcys22/pdf-generator/master/pdfgenerator.js"); err != nil { - panic(err) + return fmt.Errorf("downloading pdfgenerator.js failed (%v)", err) } filename := "./src/financials.html" @@ -142,7 +143,7 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a log.Debugf("Downloading template: %s", httpfile) if err := DownloadFile(filename, httpfile); err != nil { - panic(err) + return fmt.Errorf("downloading template %s failed (%v)", httpfile, err) } command := "node ./pdfgenerator.js" @@ -155,11 +156,11 @@ requires Nodejs on the machine and also handlebars (npm install -g handlebars) a //Restructure and Cleanup err = os.Rename("src/mypdf.pdf", ctx.String("template")+".pdf") if err != nil { - panic(err) + return fmt.Errorf("renaming file failed (%v)", err) } err = os.RemoveAll("src") if err != nil { - panic(err) + return fmt.Errorf("removing src folder failed (%v)", err) } return nil @@ -171,14 +172,14 @@ func DownloadFile(filepath string, url string) error { // Get the data resp, err := http.Get(url) if err != nil { - return err + return fmt.Errorf("downloading %s failed (%v)", url, err) } defer resp.Body.Close() // Create the file out, err := os.Create(filepath) if err != nil { - return err + return fmt.Errorf("creating file %s failed (%v)", filepath, err) } defer out.Close() diff --git a/reporter/transactionlisting.go b/reporter/transactionlisting.go index 4362140..226e60c 100644 --- a/reporter/transactionlisting.go +++ b/reporter/transactionlisting.go @@ -2,7 +2,9 @@ package main import ( "fmt" + "math" "os" + "strconv" "time" "encoding/csv" @@ -12,7 +14,6 @@ import ( "github.com/darcys22/godbledger/godbledger/ledger" "github.com/olekukonko/tablewriter" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" ) @@ -46,11 +47,11 @@ If you want to see all the transactions in the database, or export to CSV/JSON err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } ledger, err := ledger.New(ctx, cfg) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make new ledger (%v)", err) } table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Date", "ID", "Account", "Description", "Currency", "Amount"}) @@ -65,12 +66,14 @@ If you want to see all the transactions in the database, or export to CSV/JSON splits.split_date, splits.description, splits.currency, + currency.decimals, splits.amount, split_accounts.account_id FROM splits JOIN split_accounts ON splits.split_id = split_accounts.split_id JOIN transactions on splits.transaction_id = transactions.transaction_id + JOIN currencies AS currency ON splits.currency = currency.NAME WHERE splits.split_date >= ? AND splits.split_date <= ? @@ -89,21 +92,27 @@ If you want to see all the transactions in the database, or export to CSV/JSON rows, err := ledger.LedgerDb.Query(queryDB, queryDateStart, queryDateEnd) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not query database (%v)", err) } defer rows.Close() for rows.Next() { // Scan one customer record var t Transaction - if err := rows.Scan(&t.ID, &t.Date, &t.Description, &t.Currency, &t.Amount, &t.Account); err != nil { - return err + var decimals float64 + if err := rows.Scan(&t.ID, &t.Date, &t.Description, &t.Currency, &decimals, &t.Amount, &t.Account); err != nil { + return fmt.Errorf("Could not scan rows of query (%v)", err) } + centsAmount, err := strconv.ParseFloat(t.Amount, 64) + if err != nil { + return fmt.Errorf("Could not process the amount as a float (%v)", err) + } + t.Amount = fmt.Sprintf("%.2f", centsAmount/math.Pow(10, decimals)) output.Data = append(output.Data, t) table.Append([]string{t.Date, t.ID, t.Account, t.Description, t.Currency, t.Amount}) } if rows.Err() != nil { - return err + return fmt.Errorf("rows errored with (%v)", rows.Err()) } //Output some information. @@ -114,7 +123,7 @@ If you want to see all the transactions in the database, or export to CSV/JSON defer file.Close() if err != nil { - os.Exit(1) + return fmt.Errorf("opening csv file errored with (%v)", err) } csvWriter := csv.NewWriter(file) @@ -124,7 +133,7 @@ If you want to see all the transactions in the database, or export to CSV/JSON for _, element := range output.Data { err := csvWriter.Write([]string{element.Date, element.ID, element.Account, element.Description, element.Currency, element.Amount}) if err != nil { - log.Fatal("Cannot write to file", err) + return fmt.Errorf("could not write to csv file (%v)", err) } } @@ -133,17 +142,17 @@ If you want to see all the transactions in the database, or export to CSV/JSON file, err := os.OpenFile(ctx.String(jsonFlag.Name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { - log.Fatalf("Cannot open to file: %s", err) + return fmt.Errorf("could not open json file (%v)", err) } defer file.Close() bytes, err := json.Marshal(output.Data) if err != nil { - log.Fatal("Cannot serialize") + return fmt.Errorf("could not serialise json (%v)", err) } _, err = file.Write(bytes) if err != nil { - log.Fatal("Cannot write to file", err) + return fmt.Errorf("could not write to json file (%v)", err) } } else { diff --git a/reporter/trialbalance.go b/reporter/trialbalance.go index 6c9c74f..20915b1 100644 --- a/reporter/trialbalance.go +++ b/reporter/trialbalance.go @@ -2,7 +2,9 @@ package main import ( "fmt" + "math" "os" + "strconv" "time" "encoding/csv" @@ -12,7 +14,6 @@ import ( "github.com/darcys22/godbledger/godbledger/ledger" "github.com/olekukonko/tablewriter" - //"github.com/urfave/cli" "github.com/urfave/cli/v2" ) @@ -41,12 +42,12 @@ If you want to see all the transactions in the database, or export to CSV //Check if keyfile path given and make sure it doesn't already exist. err, cfg := cmd.MakeConfig(ctx) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make config (%v)", err) } ledger, err := ledger.New(ctx, cfg) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not make new ledger (%v)", err) } queryDate := time.Now() @@ -56,37 +57,45 @@ If you want to see all the transactions in the database, or export to CSV queryDB := ` SELECT split_accounts.account_id, - Sum(splits.amount) + Sum(splits.amount), + currency.decimals FROM splits - JOIN split_accounts - ON splits.split_id = split_accounts.split_id + JOIN split_accounts ON splits.split_id = split_accounts.split_id + JOIN currencies AS currency ON splits.currency = currency.NAME WHERE splits.split_date <= ? AND "void" NOT IN (SELECT t.tag_name - FROM tags AS t - JOIN transaction_tag AS tt - ON tt.tag_id = t.tag_id - WHERE tt.transaction_id = splits.transaction_id) - GROUP BY split_accounts.account_id + FROM tags AS t + JOIN transaction_tag AS tt + ON tt.tag_id = t.tag_id + WHERE tt.transaction_id = splits.transaction_id) + GROUP BY split_accounts.account_id, splits.currency + ;` log.Debug("Querying Database") rows, err := ledger.LedgerDb.Query(queryDB, queryDate) if err != nil { - log.Fatal(err) + return fmt.Errorf("Could not query database (%v)", err) } defer rows.Close() for rows.Next() { // Scan one customer record var t Account - if err := rows.Scan(&t.Account, &t.Amount); err != nil { - // handle error + var decimals float64 + if err := rows.Scan(&t.Account, &t.Amount, &decimals); err != nil { + return fmt.Errorf("Could not scan rows of query (%v)", err) + } + centsAmount, err := strconv.ParseFloat(t.Amount, 64) + if err != nil { + return fmt.Errorf("Could not process the amount as a float (%v)", err) } + t.Amount = fmt.Sprintf("%.2f", centsAmount/math.Pow(10, decimals)) tboutput.Data = append(tboutput.Data, t) table.Append([]string{t.Account, t.Amount}) } if rows.Err() != nil { - // handle error + return fmt.Errorf("rows errored with (%v)", rows.Err()) } //Output some information. @@ -94,7 +103,7 @@ If you want to see all the transactions in the database, or export to CSV log.Infof("Exporting CSV to %s", ctx.String(csvFlag.Name)) file, err := os.OpenFile(ctx.String(csvFlag.Name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { - log.Fatalf("Cannot open to file: %s", err) + return fmt.Errorf("opening csv file errored with (%v)", err) } defer file.Close() @@ -105,7 +114,7 @@ If you want to see all the transactions in the database, or export to CSV for _, element := range tboutput.Data { err := csvWriter.Write([]string{element.Account, element.Amount}) if err != nil { - log.Fatal("Cannot write to file", err) + return fmt.Errorf("could not write to csv file (%v)", err) } } @@ -114,17 +123,17 @@ If you want to see all the transactions in the database, or export to CSV file, err := os.OpenFile(ctx.String(jsonFlag.Name), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { - log.Fatalf("Cannot open to file: %s", err) + return fmt.Errorf("could not open json file (%v)", err) } defer file.Close() bytes, err := json.Marshal(tboutput.Data) if err != nil { - log.Fatal("Cannot serialize") + return fmt.Errorf("could not serialise json (%v)", err) } _, err = file.Write(bytes) if err != nil { - log.Fatal("Cannot write to file", err) + return fmt.Errorf("could not write to json file (%v)", err) } } else { fmt.Println() diff --git a/shared/logscontain.go b/shared/logscontain.go new file mode 100644 index 0000000..38027f9 --- /dev/null +++ b/shared/logscontain.go @@ -0,0 +1,60 @@ +package shared + +import ( + "fmt" + "path/filepath" + "runtime" + "strings" + + logTest "github.com/sirupsen/logrus/hooks/test" +) + +type assertionLoggerFn func(string, ...interface{}) + +func parseMsg(defaultMsg string, msg ...interface{}) string { + if len(msg) >= 1 { + msgFormat, ok := msg[0].(string) + if !ok { + return defaultMsg + } + return fmt.Sprintf(msgFormat, msg[1:]...) + } + return defaultMsg +} + +// LogsContain checks whether a given substring is a part of logs. If flag=false, inverse is checked. +func LogsContain(loggerFn assertionLoggerFn, hook *logTest.Hook, want string, flag bool, msg ...interface{}) { + _, file, line, _ := runtime.Caller(2) + entries := hook.AllEntries() + var logs []string + match := false + for _, e := range entries { + msg, err := e.String() + if err != nil { + loggerFn("%s:%d Failed to format log entry to string: %v", filepath.Base(file), line, err) + return + } + if strings.Contains(msg, want) { + match = true + } + for _, field := range e.Data { + fieldStr, ok := field.(string) + if !ok { + continue + } + if strings.Contains(fieldStr, want) { + match = true + } + } + logs = append(logs, msg) + } + var errMsg string + if flag && !match { + errMsg = parseMsg("Expected log not found", msg...) + } else if !flag && match { + errMsg = parseMsg("Unexpected log found", msg...) + } + if errMsg != "" { + loggerFn("%s:%d %s: %v\nSearched logs:\n%v", filepath.Base(file), line, errMsg, want, logs) + } +} diff --git a/tests/evaluators/operations.go b/tests/evaluators/operations.go index 2991cbf..227c320 100644 --- a/tests/evaluators/operations.go +++ b/tests/evaluators/operations.go @@ -45,7 +45,6 @@ func singleTransaction(conns ...*grpc.ClientConn) error { Date: date.Format("2006-01-02"), Description: desc, Lines: transactionLines, - Signature: "test", } _, err := client.AddTransaction(context.Background(), req) if err != nil { diff --git a/tests/testdata/BasicTests/add1.json b/tests/testdata/BasicTests/add1.json index 59b4318..c3bf85f 100644 --- a/tests/testdata/BasicTests/add1.json +++ b/tests/testdata/BasicTests/add1.json @@ -40,8 +40,7 @@ "Currency": "USD", "Balance": "-100" } - ], - "Signature": "stuff" + ] } ] } diff --git a/utils/testdata.go b/utils/testdata.go index 40ae278..47b48c2 100644 --- a/utils/testdata.go +++ b/utils/testdata.go @@ -111,7 +111,6 @@ func doInstall(jsonfile string) { Date: date, Payee: obj[i].Description[:20], AccountChanges: transactionLines, - Signature: "stuff", } err = Send(req) @@ -149,7 +148,6 @@ type Transaction struct { Payee string Date time.Time AccountChanges []Account - Signature string } type sortTransactions []*Transaction @@ -191,7 +189,6 @@ func Send(t *Transaction) error { Date: t.Date.Format("2006-01-02"), Description: t.Payee, Lines: transactionLines, - Signature: t.Signature, } r, err := client.AddTransaction(ctx, req) if err != nil {