diff --git a/Dockerfile b/Dockerfile index 191bc33..7c09597 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,8 +21,7 @@ RUN make VISE_PATH=/build/go-vise -B WORKDIR /build/sarafu-vise RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM" RUN go mod download -RUN go build -tags logtrace,online -o sarafu-at -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go -RUN go build -tags logtrace,online -o sarafu-ssh -ldflags="-X main.build=${BUILD} -s -w" cmd/ssh/main.go +RUN go build -tags logdebug,online -o sarafu-at -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go FROM debian:bookworm-slim @@ -37,7 +36,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ WORKDIR /service COPY --from=build /build/sarafu-vise/sarafu-at . -COPY --from=build /build/sarafu-vise/sarafu-ssh . COPY --from=build /build/sarafu-vise/LICENSE . COPY --from=build /build/sarafu-vise/README.md . COPY --from=build /build/sarafu-vise/services ./services @@ -45,6 +43,5 @@ COPY --from=build /build/sarafu-vise/.env.example . RUN mv .env.example .env EXPOSE 7123 -EXPOSE 7122 CMD ["./sarafu-at"] diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 3f7e372..2f34b56 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -44,6 +44,7 @@ func main() { var err error var gettextDir string var langs args.LangVar + var logDbConnStr string flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") @@ -55,6 +56,7 @@ func main() { flag.UintVar(&port, "p", config.Port(), "http port") flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") flag.Var(&langs, "language", "add symbol resolution for language") + flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string") flag.Parse() config.Apply(override) @@ -100,6 +102,11 @@ func main() { fmt.Fprintf(os.Stderr, "userdatadb: %v\n", err) os.Exit(1) } + logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data") + if err != nil { + fmt.Fprintf(os.Stderr, "get log db error: %v\n", err) + os.Exit(1) + } dbResource, ok := rs.(*resource.DbResource) if !ok { @@ -113,6 +120,7 @@ func main() { os.Exit(1) } lhs.SetDataStore(&userdataStore) + lhs.SetLogDb(&logdb) if err != nil { fmt.Fprintf(os.Stderr, "setdatastore: %v\n", err) os.Exit(1) diff --git a/cmd/async/main.go b/cmd/async/main.go index 9a064a6..5bb6816 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -56,6 +56,7 @@ func main() { var err error var gettextDir string var langs args.LangVar + var logDbConnStr string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") @@ -69,6 +70,7 @@ func main() { flag.UintVar(&port, "p", config.Port(), "http port") flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") flag.Var(&langs, "language", "add symbol resolution for language") + flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string") flag.Parse() config.Apply(override) @@ -120,6 +122,12 @@ func main() { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } + + logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data") + if err != nil { + fmt.Fprintf(os.Stderr, "get log db error: %v\n", err) + os.Exit(1) + } //defer userdataStore.Close(ctx) dbResource, ok := rs.(*resource.DbResource) @@ -129,6 +137,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) + lhs.SetLogDb(&logdb) accountService := services.New(ctx, menuStorageService) hl, err := lhs.GetHandler(accountService) diff --git a/cmd/http/main.go b/cmd/http/main.go index 13ef408..eed729a 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -43,6 +43,7 @@ func main() { var err error var gettextDir string var langs args.LangVar + var logDbConnStr string flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") flag.StringVar(&override.UserConn, "userdata", "?", "userdata store connection string") @@ -55,6 +56,7 @@ func main() { flag.UintVar(&port, "p", config.Port(), "http port") flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") flag.Var(&langs, "language", "add symbol resolution for language") + flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string") flag.Parse() config.Apply(override) @@ -103,6 +105,12 @@ func main() { os.Exit(1) } + logdb, err := menuStorageService.GetLogDb(ctx, userdataStore, logDbConnStr, "user-data") + if err != nil { + fmt.Fprintf(os.Stderr, "get log db error: %v\n", err) + os.Exit(1) + } + dbResource, ok := rs.(*resource.DbResource) if !ok { os.Exit(1) @@ -110,6 +118,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) + lhs.SetLogDb(&logdb) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/cmd/main.go b/cmd/main.go index a18bfab..1b40e32 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -36,6 +36,7 @@ func main() { var err error var gettextDir string var langs args.LangVar + var logDbConnStr string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&override.DbConn, "c", "?", "default connection string (replaces all unspecified strings)") @@ -46,6 +47,7 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory") flag.Var(&langs, "language", "add symbol resolution for language") + flag.StringVar(&logDbConnStr, "log-c", "db-logs", "log db connection string") flag.Parse() config.Apply(override) @@ -110,6 +112,12 @@ func main() { os.Exit(1) } + logdb, err := menuStorageService.GetLogDb(ctx, userdatastore, logDbConnStr, "user-data") + if err != nil { + fmt.Fprintf(os.Stderr, "get log db error: %v\n", err) + os.Exit(1) + } + dbResource, ok := rs.(*resource.DbResource) if !ok { fmt.Fprintf(os.Stderr, "get dbresource error: %v\n", err) @@ -118,6 +126,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdatastore) + lhs.SetLogDb(&logdb) lhs.SetPersister(pe) if err != nil { fmt.Fprintf(os.Stderr, "localhandler service error: %v\n", err) diff --git a/go.mod b/go.mod index 531e4b0..713e0c4 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module git.grassecon.net/grassrootseconomics/sarafu-vise go 1.23.4 require ( - git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac - git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d - git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250313071411-ba43610ff00b - git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244 + git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9 + git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e + git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f + git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 github.com/alecthomas/assert/v2 v2.2.2 github.com/gofrs/uuid v4.4.0+incompatible @@ -19,24 +19,50 @@ require ( ) require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect github.com/alecthomas/participle/v2 v2.0.0 // indirect github.com/alecthomas/repr v0.2.0 // indirect github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect + github.com/bits-and-blooms/bitset v1.14.3 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect + github.com/ethereum/go-ethereum v1.14.9 // indirect + github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 // indirect github.com/fxamacker/cbor/v2 v2.4.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/grassrootseconomics/eth-custodial v1.3.0-beta // indirect + github.com/grassrootseconomics/ethutils v1.3.1 // indirect github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect + github.com/holiman/uint256 v1.3.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/joho/godotenv v1.5.1 // indirect + github.com/lmittmann/w3 v0.17.1 // indirect github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.7.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect ) diff --git a/go.sum b/go.sum index b29d7a1..ec69f0a 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,19 @@ -git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac h1:f/E0ZTclVfMEnD/3Alrzzbg+dOm138zGydV42jT0JPw= -git.defalsify.org/vise.git v0.2.3-0.20250205173834-d1f6647211ac/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= -git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d h1:5mzLas+jxTUtusOKx4XvU+n2QvrV/mH17MnJRy46siQ= -git.grassecon.net/grassrootseconomics/common v0.0.0-20250121134736-ba8cbbccea7d/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69 h1:cbBpm9uNJak58MpFpNXJuvgCmz+A8kquXr9har4expg= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250206112944-31eb30de0f69/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250224114958-1f3ac220d126 h1:fXL9uXt8yiu5ImJnwiHoLsq8T2TJ6+8qa97pCt+gHxA= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250224114958-1f3ac220d126/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250306151434-35f24b518375 h1:VilYLeBFXxOSgs+AD9B1+Mu+VJZHiSWACY5xF1x3xBE= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250306151434-35f24b518375/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250307051649-4783e2dcb319 h1:Qg6ttFJ4zjF67PERvLcXJ4/bXUTdCaRJ96RFRmQZ4xc= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250307051649-4783e2dcb319/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310110330-a04f7ee66c35 h1:JeRpSf+/NTFTl3lhiSkHO54NIoK6KrXmyqawfiuKc0Y= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310110330-a04f7ee66c35/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310111703-e3b6c25792c2 h1:QhiSpx3ndgQeR/CAynLdiOR+wNEJJDFWMKE2CHmp8n8= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310111703-e3b6c25792c2/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310133937-ba8d2a19c2ed h1:r5w89jInk9wwJZ4kMmE8ENPV7chxORJWIfxsPmnAQwY= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310133937-ba8d2a19c2ed/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310141205-3b39b86d0987 h1:HXFD3Pabi1H4tuKVZUjyqsYoI8JrhJrH0NXeVuB2buo= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250310141205-3b39b86d0987/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250311124651-3244c717cbee h1:YONuCBfzDlpeXsQMWRiBrRlSC0YHWUNuC5Sit43Ncww= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250311124651-3244c717cbee/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250313070549-3c2889ac72f0 h1:AMa2VmeRrmHz87+RtH9sJGTGnlYXqkhJG1tX/vgQAx0= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250313070549-3c2889ac72f0/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250313071411-ba43610ff00b h1:+j02QfOVYvPteaIpL/3KQ6taQnoI7TFkcH23iBUhoZg= -git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250313071411-ba43610ff00b/go.mod h1:K/TPgZ4OhPHBQq2X0ab3JZs4YjiexzSURZcfHLs9Pf4= -git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244 h1:BXotWSKg04U97sf/xeWJuUTSVgKk2aEK+5BtBrnafXQ= -git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.1.0.20250204132347-1eb0b1555244/go.mod h1:6B6ByxXOiRY0NR7K02Bf3fEu7z+2c/6q8PFVNjC5G8w= +git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce h1:Uke2jQ4wG97gQKnTzxPyBGyhosrU1IWnRNFHtKVrmrk= +git.defalsify.org/vise.git v0.3.2-0.20250425131748-8b84f59792ce/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9 h1:4kjbYw25MHZe9fqSbujPzpFXrYutFfVipvLrcWYnYks= +git.defalsify.org/vise.git v0.3.2-0.20250507172020-cb22240f1cb9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e h1:DcC9qkNl9ny3hxQmsMK6W81+5R/j4ZwYUbvewMI/rlc= +git.grassecon.net/grassrootseconomics/common v0.9.0-beta.1.0.20250417111317-2953f4c2f32e/go.mod h1:wgQJZGIS6QuNLHqDhcsvehsbn5PvgV7aziRebMnJi60= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f h1:OAHCP3YR1C5h1WFnnEnLs5kn6jTxQHQYWYtQaMZJIMY= +git.grassecon.net/grassrootseconomics/sarafu-api v0.9.0-beta.1.0.20250428082711-5d221b8d565f/go.mod h1:gOn89ipaDcDvmQXRMQYKUqcw/sJcwVOPVt2eC6Geip8= +git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306 h1:Jo+yWysWw/N5BJQtAyEMN8ePVvAyPHv+JG4lQti+1N4= +git.grassecon.net/grassrootseconomics/visedriver v0.9.0-beta.2.0.20250408094335-e2d1f65bb306/go.mod h1:FdLwYtzsjOIcDiW4uDgDYnB4Wdzq12uJUe0QHSSPbSo= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694 h1:DjJlBSz0S13acft5XZDWk7ZYnzElym0xLMYEVgyNJ+E= git.grassecon.net/grassrootseconomics/visedriver-africastalking v0.0.0-20250129070628-5a539172c694/go.mod h1:DpibtYpnT3nG4Kn556hRAkdu4+CtiI/6MbnQHal51mQ= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g= @@ -36,15 +22,46 @@ github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE= github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U= +github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA= +github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= +github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= +github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.14.9 h1:J7iwXDrtUyE9FUjUYbd4c9tyzwMh6dTJsKzo9i6SrwA= +github.com/ethereum/go-ethereum v1.14.9/go.mod h1:QeW+MtTpRdBEm2pUFoonByee8zfHv7kGp0wK0odvU1I= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= +github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU= github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo= +github.com/grassrootseconomics/ethutils v1.3.1 h1:LlQO90HjJkl7ejC8fv6jP7RJUrAm1j4VMMCYfsoIrhU= +github.com/grassrootseconomics/ethutils v1.3.1/go.mod h1:Wuv1VEZrkLIXqTSEYI3Nh9HG/ZHOUQ+U+xvWJ8QtjgQ= github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk= github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0= github.com/grassrootseconomics/ussd-data-service v1.4.0-beta h1:4fMd/3h2ZIhRg4GdHQmRw5FfD3MpJvFNNJQo+Q27f5M= @@ -53,6 +70,8 @@ github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qf github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -67,8 +86,13 @@ github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lmittmann/w3 v0.17.1 h1:zdXIimmNmYfqOFur+Jqc9Yqwtq6jwnsQufbTOnSAtW4= +github.com/lmittmann/w3 v0.17.1/go.mod h1:WVUGMbL83WYBu4Sge3SVlW3qIG4VaHe+S8+UUnwz9Eg= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY= github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s= github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A= github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I= @@ -78,6 +102,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -85,18 +111,32 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -105,3 +145,5 @@ gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2Xj 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= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/handlers/application/menuhandler.go b/handlers/application/menuhandler.go index 8adeee7..3aa1073 100644 --- a/handlers/application/menuhandler.go +++ b/handlers/application/menuhandler.go @@ -1,11 +1,14 @@ package application import ( + "bytes" "context" + "errors" "fmt" "path" "strconv" "strings" + "unicode" "gopkg.in/leonelquinteros/gotext.v1" @@ -26,9 +29,11 @@ import ( "git.grassecon.net/grassrootseconomics/sarafu-api/models" "git.grassecon.net/grassrootseconomics/sarafu-api/remote" "git.grassecon.net/grassrootseconomics/sarafu-vise/config" + "git.grassecon.net/grassrootseconomics/sarafu-vise/internal/sms" "git.grassecon.net/grassrootseconomics/sarafu-vise/profile" "git.grassecon.net/grassrootseconomics/sarafu-vise/store" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" + "github.com/grassrootseconomics/ethutils" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -74,18 +79,28 @@ type MenuHandlers struct { flagManager *FlagManager accountService remote.AccountService prefixDb storedb.PrefixDb + smsService sms.SmsService + logDb store.LogDb profile *profile.Profile ReplaceSeparatorFunc func(string) string } // NewHandlers creates a new instance of the Handlers struct with the provided dependencies. -func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, accountService remote.AccountService, replaceSeparatorFunc func(string) string) (*MenuHandlers, error) { +func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, logdb db.Db, accountService remote.AccountService, replaceSeparatorFunc func(string) string) (*MenuHandlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } userDb := &store.UserDataStore{ Db: userdataStore, } + smsservice := sms.SmsService{ + Accountservice: accountService, + Userdatastore: *userDb, + } + + logDb := store.LogDb{ + Db: logdb, + } // Instantiate the SubPrefixDb with "DATATYPE_USERDATA" prefix prefix := storedb.ToBytes(db.DATATYPE_USERDATA) @@ -95,7 +110,9 @@ func NewMenuHandlers(appFlags *FlagManager, userdataStore db.Db, accountService userdataStore: userDb, flagManager: appFlags, accountService: accountService, + smsService: smsservice, prefixDb: prefixDb, + logDb: logDb, profile: &profile.Profile{Max: 6}, ReplaceSeparatorFunc: replaceSeparatorFunc, } @@ -183,23 +200,35 @@ func (h *MenuHandlers) SetLanguage(ctx context.Context, sym string, input []byte // handles the account creation when no existing account is present for the session and stores associated data in the user data store. func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") + flag_account_creation_failed, _ := h.flagManager.GetFlag("flag_account_creation_failed") + r, err := h.accountService.CreateAccount(ctx) if err != nil { - return err + res.FlagSet = append(res.FlagSet, flag_account_creation_failed) + logg.ErrorCtxf(ctx, "failed to create an account", "error", err) + return nil } + res.FlagReset = append(res.FlagReset, flag_account_creation_failed) + trackingId := r.TrackingId publicKey := r.PublicKey data := map[storedb.DataTyp]string{ - storedb.DATA_TRACKING_ID: trackingId, - storedb.DATA_PUBLIC_KEY: publicKey, + storedb.DATA_TRACKING_ID: trackingId, + storedb.DATA_PUBLIC_KEY: publicKey, + storedb.DATA_ACCOUNT_ALIAS: "", } store := h.userdataStore + logdb := h.logDb for key, value := range data { err = store.WriteEntry(ctx, sessionId, key, []byte(value)) if err != nil { return err } + err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write log entry", "key", key, "value", value) + } } publicKeyNormalized, err := hex.NormalizeHex(publicKey) if err != nil { @@ -209,6 +238,12 @@ func (h *MenuHandlers) createAccountNoExist(ctx context.Context, sessionId strin if err != nil { return err } + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write log entry", "key", storedb.DATA_PUBLIC_KEY_REVERSE, "value", sessionId) + } + res.FlagSet = append(res.FlagSet, flag_account_created) return nil } @@ -239,26 +274,60 @@ func (h *MenuHandlers) CreateAccount(ctx context.Context, sym string, input []by return res, nil } -// ResetValidPin resets the flag_valid_pin flag. -func (h *MenuHandlers) ResetValidPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { +func (h *MenuHandlers) CheckAccountCreated(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") - res.FlagReset = append(res.FlagReset, flag_valid_pin) + flag_language_set, _ := h.flagManager.GetFlag("flag_language_set") + flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") + + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + _, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + // reset major flags + res.FlagReset = append(res.FlagReset, flag_language_set) + res.FlagReset = append(res.FlagReset, flag_account_created) + + return res, nil + } + + return res, nil + } + + res.FlagSet = append(res.FlagSet, flag_account_created) return res, nil } -// CheckBlockedStatus resets the account blocked flag if the PIN attempts have been reset by an admin. +// CheckBlockedStatus: +// 1. Checks whether the DATA_SELF_PIN_RESET is 1 and sets the flag_account_pin_reset +// 2. resets the account blocked flag if the PIN attempts have been reset by an admin. func (h *MenuHandlers) CheckBlockedStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result store := h.userdataStore flag_account_blocked, _ := h.flagManager.GetFlag("flag_account_blocked") + flag_account_pin_reset, _ := h.flagManager.GetFlag("flag_account_pin_reset") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } + res.FlagReset = append(res.FlagReset, flag_account_pin_reset) + + selfPinReset, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SELF_PIN_RESET) + if err == nil { + pinResetValue, _ := strconv.ParseUint(string(selfPinReset), 0, 64) + if pinResetValue == 1 { + res.FlagSet = append(res.FlagSet, flag_account_pin_reset) + } + } + currentWrongPinAttempts, err := store.ReadEntry(ctx, sessionId, storedb.DATA_INCORRECT_PIN_ATTEMPTS) if err != nil { if !db.IsNotFound(err) { @@ -267,7 +336,6 @@ func (h *MenuHandlers) CheckBlockedStatus(ctx context.Context, sym string, input } pinAttemptsValue, _ := strconv.ParseUint(string(currentWrongPinAttempts), 0, 64) - if pinAttemptsValue == 0 { res.FlagReset = append(res.FlagReset, flag_account_blocked) return res, nil @@ -310,29 +378,6 @@ func (h *MenuHandlers) ResetIncorrectPin(ctx context.Context, sym string, input return res, nil } -// VerifyNewPin checks if a new PIN meets the required format criteria. -func (h *MenuHandlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { - res := resource.Result{} - _, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") - if string(input) != "0" { - pinInput := string(input) - // Validate that the PIN is a 4-digit number. - if pin.IsValidPIN(pinInput) { - res.FlagSet = append(res.FlagSet, flag_valid_pin) - } else { - res.FlagReset = append(res.FlagReset, flag_valid_pin) - } - } else { - res.FlagSet = append(res.FlagSet, flag_valid_pin) - } - - return res, nil -} - // SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE, // during the account creation process // and during the change PIN process. @@ -345,15 +390,20 @@ func (h *MenuHandlers) SaveTemporaryPin(ctx context.Context, sym string, input [ return res, fmt.Errorf("missing session") } - flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - accountPIN := string(input) + flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") - // Validate that the PIN is a 4-digit number. - if !pin.IsValidPIN(accountPIN) { - res.FlagSet = append(res.FlagSet, flag_incorrect_pin) + if string(input) == "0" { return res, nil } - res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + + accountPIN := string(input) + + // Validate that the PIN has a valid format. + if !pin.IsValidPIN(accountPIN) { + res.FlagSet = append(res.FlagSet, flag_invalid_pin) + return res, nil + } + res.FlagReset = append(res.FlagReset, flag_invalid_pin) // Hash the PIN hashedPIN, err := pin.HashPIN(string(accountPIN)) @@ -363,93 +413,35 @@ func (h *MenuHandlers) SaveTemporaryPin(ctx context.Context, sym string, input [ } store := h.userdataStore + logdb := h.logDb + err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN)) if err != nil { logg.ErrorCtxf(ctx, "failed to write temporaryAccountPIN entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) return res, err } + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write temporaryAccountPIN log entry", "key", storedb.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) + } + return res, nil } -// SaveOthersTemporaryPin allows authorized users to set temporary PINs for blocked numbers. -func (h *MenuHandlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { +// ResetInvalidPIN resets the invalid PIN flag +func (h *MenuHandlers) ResetInvalidPIN(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error - - store := h.userdataStore - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - temporaryPin := string(input) - - // Validate that the input is a 4-digit number. - if !pin.IsValidPIN(temporaryPin) { - return res, nil - } - - // Retrieve the blocked number associated with this session - blockedNumber, err := store.ReadEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err) - return res, err - } - - // Hash the temporary PIN - hashedPIN, err := pin.HashPIN(string(temporaryPin)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) - return res, err - } - - // Save the hashed temporary PIN for that blocked number - err = store.WriteEntry(ctx, string(blockedNumber), storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write hashed temporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) - return res, err - } - + flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") + res.FlagReset = append(res.FlagReset, flag_invalid_pin) return res, nil } -// CheckBlockedNumPinMisMatch checks if the provided PIN matches a temporary PIN stored for a blocked number. -func (h *MenuHandlers) CheckBlockedNumPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) { - res := resource.Result{} - flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - if string(input) == "0" { - res.FlagReset = append(res.FlagReset, flag_pin_mismatch) - return res, nil - } - - // Get blocked number from storage. - store := h.userdataStore - blockedNumber, err := store.ReadEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err) - return res, err - } - // Get Hashed temporary PIN for the blocked number. - hashedTemporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), storedb.DATA_TEMPORARY_VALUE) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err) - return res, err - } - if len(hashedTemporaryPin) == 0 { - logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE) - return res, fmt.Errorf("Data error encountered") - } - - if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) { - res.FlagReset = append(res.FlagReset, flag_pin_mismatch) - } else { - res.FlagSet = append(res.FlagSet, flag_pin_mismatch) - } +// ResetApiCallFailure resets the api call failure flag +func (h *MenuHandlers) ResetApiCallFailure(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + res.FlagReset = append(res.FlagReset, flag_api_error) return res, nil } @@ -461,6 +453,7 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [ return res, fmt.Errorf("missing session") } flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + flag_account_pin_reset, _ := h.flagManager.GetFlag("flag_account_pin_reset") if string(input) == "0" { res.FlagReset = append(res.FlagReset, flag_pin_mismatch) @@ -468,6 +461,7 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [ } store := h.userdataStore + logdb := h.logDb hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) if err != nil { logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err) @@ -492,16 +486,85 @@ func (h *MenuHandlers) ConfirmPinChange(ctx context.Context, sym string, input [ return res, err } + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write AccountPIN log entry", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err) + } + + // set the DATA_SELF_PIN_RESET as 0 + err = store.WriteEntry(ctx, sessionId, storedb.DATA_SELF_PIN_RESET, []byte("0")) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write DATA_SELF_PIN_RESET entry with", "key", storedb.DATA_SELF_PIN_RESET, "self PIN reset value", "0", "error", err) + return res, err + } + res.FlagReset = append(res.FlagReset, flag_account_pin_reset) + + return res, nil +} + +// ValidateBlockedNumber performs validation of phone numbers during the Reset other's PIN. +// It checks phone number format and verifies registration status. +// If valid, it writes the number under DATA_BLOCKED_NUMBER on the admin account +func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + + flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") + store := h.userdataStore + logdb := h.logDb + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + if string(input) == "0" { + res.FlagReset = append(res.FlagReset, flag_unregistered_number) + return res, nil + } + + blockedNumber := string(input) + formattedNumber, err := phone.FormatPhoneNumber(blockedNumber) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_unregistered_number) + logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", blockedNumber, "error", err) + return res, nil + } + + _, err = store.ReadEntry(ctx, formattedNumber, storedb.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + logg.InfoCtxf(ctx, "Invalid or unregistered number") + res.FlagSet = append(res.FlagSet, flag_unregistered_number) + return res, nil + } else { + logg.ErrorCtxf(ctx, "Error on ValidateBlockedNumber", "error", err) + return res, err + } + } + + err = store.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(formattedNumber)) + if err != nil { + return res, nil + } + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(formattedNumber)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write blocked number log entry", "key", storedb.DATA_BLOCKED_NUMBER, "value", formattedNumber, "error", err) + } + return res, nil } // ResetOthersPin handles the PIN reset process for other users' accounts by: // 1. Retrieving the blocked phone number from the session -// 2. Fetching the hashed temporary PIN associated with that number -// 3. Updating the account PIN with the temporary PIN +// 2. Writing the DATA_SELF_PIN_RESET on the blocked phone number +// 3. Resetting the DATA_INCORRECT_PIN_ATTEMPTS to 0 for the blocked phone number func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + store := h.userdataStore + smsservice := h.smsService + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -511,19 +574,11 @@ func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []b logg.ErrorCtxf(ctx, "failed to read blockedPhonenumber entry with", "key", storedb.DATA_BLOCKED_NUMBER, "error", err) return res, err } - hashedTemporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), storedb.DATA_TEMPORARY_VALUE) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read hashedTmporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err) - return res, err - } - if len(hashedTemporaryPin) == 0 { - logg.ErrorCtxf(ctx, "hashedTemporaryPin is empty", "key", storedb.DATA_TEMPORARY_VALUE) - return res, fmt.Errorf("Data error encountered") - } - err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin)) + // set the DATA_SELF_PIN_RESET for the account + err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_SELF_PIN_RESET, []byte("1")) if err != nil { - return res, err + return res, nil } err = store.WriteEntry(ctx, string(blockedPhonenumber), storedb.DATA_INCORRECT_PIN_ATTEMPTS, []byte(string("0"))) @@ -531,7 +586,15 @@ func (h *MenuHandlers) ResetOthersPin(ctx context.Context, sym string, input []b logg.ErrorCtxf(ctx, "failed to reset incorrect PIN attempts", "key", storedb.DATA_INCORRECT_PIN_ATTEMPTS, "error", err) return res, err } - + blockedPhoneStr := string(blockedPhonenumber) + //Trigger an SMS to inform a user that the blocked account has been reset + if phone.IsValidPhoneNumber(blockedPhoneStr) { + err = smsservice.SendPINResetSMS(ctx, sessionId, blockedPhoneStr) + if err != nil { + logg.DebugCtxf(ctx, "Failed to send PIN reset SMS", "error", err) + return res, nil + } + } return res, nil } @@ -594,65 +657,29 @@ func (h *MenuHandlers) ResetUnregisteredNumber(ctx context.Context, sym string, return res, nil } -// ValidateBlockedNumber performs validation of phone numbers, specifically for blocked numbers in the system. -// It checks phone number format and verifies registration status. -func (h *MenuHandlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error - - flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") - store := h.userdataStore - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - if string(input) == "0" { - res.FlagReset = append(res.FlagReset, flag_unregistered_number) - return res, nil - } - - blockedNumber := string(input) - formattedNumber, err := phone.FormatPhoneNumber(blockedNumber) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_unregistered_number) - logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", blockedNumber, "error", err) - return res, nil - } - - _, err = store.ReadEntry(ctx, formattedNumber, storedb.DATA_PUBLIC_KEY) - if err != nil { - if db.IsNotFound(err) { - logg.InfoCtxf(ctx, "Invalid or unregistered number") - res.FlagSet = append(res.FlagSet, flag_unregistered_number) - return res, nil - } else { - logg.ErrorCtxf(ctx, "Error on ValidateBlockedNumber", "error", err) - return res, err - } - } - err = store.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(formattedNumber)) - if err != nil { - return res, nil - } - return res, nil -} - // VerifyCreatePin checks whether the confirmation PIN is similar to the temporary PIN // If similar, it sets the USERFLAG_PIN_SET flag and writes the account PIN allowing the user // to access the main menu. func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") - flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") - flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set") - sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } + + flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") + flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set") + + if string(input) == "0" { + res.FlagReset = append(res.FlagReset, flag_pin_mismatch) + return res, nil + } + store := h.userdataStore + logdb := h.logDb + hashedTemporaryPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) if err != nil { logg.ErrorCtxf(ctx, "failed to read hashedTemporaryPin entry with", "key", storedb.DATA_TEMPORARY_VALUE, "error", err) @@ -664,20 +691,26 @@ func (h *MenuHandlers) VerifyCreatePin(ctx context.Context, sym string, input [] } if pin.VerifyPIN(string(hashedTemporaryPin), string(input)) { - res.FlagSet = []uint32{flag_valid_pin} - res.FlagReset = []uint32{flag_pin_mismatch} + res.FlagSet = append(res.FlagSet, flag_valid_pin) res.FlagSet = append(res.FlagSet, flag_pin_set) + res.FlagReset = append(res.FlagReset, flag_pin_mismatch) } else { - res.FlagSet = []uint32{flag_pin_mismatch} + res.FlagSet = append(res.FlagSet, flag_pin_mismatch) return res, nil } + // save the hashed PIN as the new account PIN err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err) return res, err } + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN, []byte(hashedTemporaryPin)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write DATA_ACCOUNT_PIN log entry", "key", storedb.DATA_ACCOUNT_PIN, "value", hashedTemporaryPin, "error", err) + } + return res, nil } @@ -690,7 +723,10 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by return res, fmt.Errorf("missing session") } firstName := string(input) + store := h.userdataStore + logdb := h.logDb + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_firstname_set, _ := h.flagManager.GetFlag("flag_firstname_set") @@ -707,11 +743,12 @@ func (h *MenuHandlers) SaveFirstname(ctx context.Context, sym string, input []by logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) return res, err } - err := h.constructAccountAlias(ctx) - if err != nil { - return res, err - } res.FlagSet = append(res.FlagSet, flag_firstname_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FIRST_NAME, []byte(temporaryFirstName)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write firtname db log entry", "key", storedb.DATA_FIRST_NAME, "value", temporaryFirstName) + } } else { if firstNameSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName)) @@ -737,6 +774,7 @@ func (h *MenuHandlers) SaveFamilyname(ctx context.Context, sym string, input []b } store := h.userdataStore + logdb := h.logDb familyName := string(input) flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") @@ -756,6 +794,11 @@ func (h *MenuHandlers) SaveFamilyname(ctx context.Context, sym string, input []b return res, err } res.FlagSet = append(res.FlagSet, flag_familyname_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write firtname db log entry", "key", storedb.DATA_FAMILY_NAME, "value", temporaryFamilyName) + } } else { if familyNameSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName)) @@ -812,6 +855,8 @@ func (h *MenuHandlers) SaveYob(ctx context.Context, sym string, input []byte) (r } yob := string(input) store := h.userdataStore + logdb := h.logDb + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_yob_set, _ := h.flagManager.GetFlag("flag_yob_set") @@ -830,6 +875,11 @@ func (h *MenuHandlers) SaveYob(ctx context.Context, sym string, input []byte) (r return res, err } res.FlagSet = append(res.FlagSet, flag_yob_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_YOB, []byte(temporaryYob)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write yob db log entry", "key", storedb.DATA_YOB, "value", temporaryYob) + } } else { if yobSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob)) @@ -855,6 +905,7 @@ func (h *MenuHandlers) SaveLocation(ctx context.Context, sym string, input []byt } location := string(input) store := h.userdataStore + logdb := h.logDb flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_location_set, _ := h.flagManager.GetFlag("flag_location_set") @@ -873,6 +924,11 @@ func (h *MenuHandlers) SaveLocation(ctx context.Context, sym string, input []byt return res, err } res.FlagSet = append(res.FlagSet, flag_location_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_LOCATION, []byte(temporaryLocation)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write location db log entry", "key", storedb.DATA_LOCATION, "value", temporaryLocation) + } } else { if locationSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location)) @@ -900,6 +956,7 @@ func (h *MenuHandlers) SaveGender(ctx context.Context, sym string, input []byte) } gender := strings.Split(symbol, "_")[1] store := h.userdataStore + logdb := h.logDb flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_gender_set, _ := h.flagManager.GetFlag("flag_gender_set") @@ -918,6 +975,12 @@ func (h *MenuHandlers) SaveGender(ctx context.Context, sym string, input []byte) return res, err } res.FlagSet = append(res.FlagSet, flag_gender_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_GENDER, []byte(temporaryGender)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write gender db log entry", "key", storedb.DATA_TEMPORARY_VALUE, "value", temporaryGender) + } + } else { if genderSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(gender)) @@ -944,6 +1007,7 @@ func (h *MenuHandlers) SaveOfferings(ctx context.Context, sym string, input []by offerings := string(input) store := h.userdataStore + logdb := h.logDb flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") flag_offerings_set, _ := h.flagManager.GetFlag("flag_offerings_set") @@ -963,6 +1027,11 @@ func (h *MenuHandlers) SaveOfferings(ctx context.Context, sym string, input []by return res, err } res.FlagSet = append(res.FlagSet, flag_offerings_set) + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_FIRST_NAME, []byte(temporaryOfferings)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write offerings db log entry", "key", storedb.DATA_OFFERINGS, "value", offerings) + } } else { if offeringsSet { err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings)) @@ -1104,6 +1173,22 @@ func (h *MenuHandlers) GetCurrentProfileInfo(ctx context.Context, sym string, in } res.FlagSet = append(res.FlagSet, flag_offerings_set) res.Content = string(profileInfo) + case storedb.DATA_ACCOUNT_ALIAS: + profileInfo, err = store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) + if err != nil { + if db.IsNotFound(err) { + res.Content = defaultValue + break + } + logg.ErrorCtxf(ctx, "Failed to read account alias entry with", "key", "error", storedb.DATA_ACCOUNT_ALIAS, err) + return res, err + } + alias := string(profileInfo) + if alias == "" { + res.Content = defaultValue + } else { + res.Content = alias + } default: break } @@ -1147,8 +1232,10 @@ func (h *MenuHandlers) GetProfileInfo(ctx context.Context, sym string, input []b offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS)) alias := getEntryOrDefault(store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS)) - if alias != defaultValue { + if alias != defaultValue && alias != "" { alias = strings.Split(alias, ".")[0] + } else { + alias = defaultValue } // Construct the full name @@ -1233,10 +1320,6 @@ func (h *MenuHandlers) UpdateAllProfileItems(ctx context.Context, sym string, in if err != nil { return res, err } - err = h.constructAccountAlias(ctx) - if err != nil { - return res, err - } return res, nil } @@ -1248,17 +1331,30 @@ func (h *MenuHandlers) ResetAccountAuthorized(ctx context.Context, sym string, i return res, nil } -// CheckIdentifier retrieves the PublicKey from the JSON data file. +// CheckIdentifier retrieves the Public key from the userdatastore under the key: DATA_PUBLIC_KEY and triggers an sms that +// will be sent to the associated session id func (h *MenuHandlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + + smsservice := h.smsService + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - + publicKey, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) + return res, err + } res.Content = string(publicKey) + //trigger an address sms to be delivered to the associated session id + err = smsservice.SendAddressSMS(ctx) + if err != nil { + logg.DebugCtxf(ctx, "Failed to trigger an address sms", "error", err) + return res, nil + } return res, nil } @@ -1275,6 +1371,7 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte) flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_invalid_pin, _ := h.flagManager.GetFlag("flag_invalid_pin") store := h.userdataStore AccountPin, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_PIN) @@ -1311,6 +1408,9 @@ func (h *MenuHandlers) Authorize(ctx context.Context, sym string, input []byte) return res, nil } } else { + if string(input) != "0" { + res.FlagSet = append(res.FlagSet, flag_invalid_pin) + } return res, nil } return res, nil @@ -1355,7 +1455,7 @@ func (h *MenuHandlers) CheckAccountStatus(ctx context.Context, sym string, input if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) logg.ErrorCtxf(ctx, "failed on TrackAccountStatus", "error", err) - return res, err + return res, nil } res.FlagReset = append(res.FlagReset, flag_api_error) @@ -1411,53 +1511,83 @@ func (h *MenuHandlers) ShowBlockedAccount(ctx context.Context, sym string, input return res, nil } +// loadUserContent loads the main user content in the main menu: the alias,balance associated with active voucher +func loadUserContent(ctx context.Context, activeSym string, balance string, alias string) (string, error) { + var content string + + code := codeFromCtx(ctx) + l := gotext.NewLocale(translationDir, code) + l.AddDomain("default") + + balFloat, err := strconv.ParseFloat(balance, 64) + if err != nil { + //Only exclude ErrSyntax error to avoid returning an error if the active bal is not available yet + if !errors.Is(err, strconv.ErrSyntax) { + logg.ErrorCtxf(ctx, "failed to parse activeBal as float", "value", balance, "error", err) + return "", err + } + balFloat = 0.00 + } + // Format to 2 decimal places + balStr := fmt.Sprintf("%.2f %s", balFloat, activeSym) + if alias != "" { + content = l.Get("%s balance: %s\n", alias, balStr) + } else { + content = l.Get("Balance: %s\n", balStr) + } + return content, nil +} + // CheckBalance retrieves the balance of the active voucher and sets // the balance as the result content. func (h *MenuHandlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - var err error + var ( + res resource.Result + err error + alias string + content string + ) sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - code := codeFromCtx(ctx) - l := gotext.NewLocale(translationDir, code) - l.AddDomain("default") - store := h.userdataStore // get the active sym and active balance activeSym, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) if err != nil { - if db.IsNotFound(err) { - balance := "0.00" - res.Content = l.Get("Balance: %s\n", balance) - return res, nil + logg.InfoCtxf(ctx, "could not find the activeSym in checkBalance:", "err", err) + if !db.IsNotFound(err) { + logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) + return res, err } - - logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) - return res, err } activeBal, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL) if err != nil { - logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) - return res, err + if !db.IsNotFound(err) { + logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", storedb.DATA_ACTIVE_BAL, "error", err) + return res, err + } } - // Convert activeBal from []byte to float64 - balFloat, err := strconv.ParseFloat(string(activeBal), 64) + accAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) if err != nil { - logg.ErrorCtxf(ctx, "failed to parse activeBal as float", "value", string(activeBal), "error", err) - return res, err + if !db.IsNotFound(err) { + logg.ErrorCtxf(ctx, "failed to read account alias entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "error", err) + return res, err + } + } else { + alias = strings.Split(string(accAlias), ".")[0] } - // Format to 2 decimal places - balStr := fmt.Sprintf("%.2f %s", balFloat, activeSym) - - res.Content = l.Get("Balance: %s\n", balStr) + content, err = loadUserContent(ctx, string(activeSym), string(activeBal), alias) + if err != nil { + return res, err + } + res.Content = content return res, nil } @@ -1491,8 +1621,10 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input } flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - recipient := string(input) + // remove white spaces + recipient := strings.ReplaceAll(string(input), " ", "") if recipient != "0" { recipientType, err := identity.CheckRecipient(recipient) @@ -1542,8 +1674,11 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input } case "address": + // checksum the address + address := ethutils.ChecksumAddress(recipient) + // Save the valid Ethereum address as the recipient - err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(recipient)) + err = store.WriteEntry(ctx, sessionId, storedb.DATA_RECIPIENT, []byte(address)) if err != nil { logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", storedb.DATA_RECIPIENT, "value", recipient, "error", err) return res, err @@ -1561,12 +1696,16 @@ func (h *MenuHandlers) ValidateRecipient(ctx context.Context, sym string, input //Perform a search for each search domain,break on first match for _, domain := range config.SearchDomains() { fqdn := fmt.Sprintf("%s.%s", recipient, domain) + logg.InfoCtxf(ctx, "Resolving with fqdn alias", "alias", fqdn) AliasAddress, err = h.accountService.CheckAliasAddress(ctx, fqdn) if err == nil { + res.FlagReset = append(res.FlagReset, flag_api_error) AliasAddressResult = AliasAddress.Address continue } else { + res.FlagSet = append(res.FlagSet, flag_api_error) logg.ErrorCtxf(ctx, "failed to resolve alias", "alias", recipient, "error_alias_check", err) + return res, nil } } } @@ -1620,6 +1759,7 @@ func (h *MenuHandlers) TransactionReset(ctx context.Context, sym string, input [ func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result store := h.userdataStore + smsservice := h.smsService sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -1630,18 +1770,23 @@ func (h *MenuHandlers) InviteValidRecipient(ctx context.Context, sym string, inp l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - recipient, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) - if len(recipient) == 0 { - logg.ErrorCtxf(ctx, "recipient is empty", "key", storedb.DATA_TEMPORARY_VALUE) - return res, fmt.Errorf("Data error encountered") + recipient, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read invalid recipient info", "error", err) + return res, err } - // TODO - // send an invitation SMS - // if successful - // res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient)) + if !phone.IsValidPhoneNumber(string(recipient)) { + logg.InfoCtxf(ctx, "corrupted recipient", "key", storedb.DATA_TEMPORARY_VALUE, "recipient", recipient) + return res, nil + } - res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient)) + _, err = smsservice.Accountservice.SendUpsellSMS(ctx, sessionId, string(recipient)) + if err != nil { + res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient)) + return res, nil + } + res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient)) return res, nil } @@ -1867,12 +2012,14 @@ func (h *MenuHandlers) InitiateTransaction(ctx context.Context, sym string, inpu return res, nil } -// SetDefaultVoucher retrieves the current vouchers -// and sets the first as the default voucher, if no active voucher is set. -func (h *MenuHandlers) SetDefaultVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { +// ManageVouchers retrieves the token holdings from the API using the "PublicKey" and +// 1. sets the first as the default voucher if no active voucher is set. +// 2. Stores list of vouchers +// 3. updates the balance of the active voucher +func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error userStore := h.userdataStore + logdb := h.logDb sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -1880,32 +2027,34 @@ func (h *MenuHandlers) SetDefaultVoucher(ctx context.Context, sym string, input } flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - // check if the user has an active sym - _, err = userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) + publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err) + return res, err + } + // Fetch vouchers from API + vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, nil + } + res.FlagReset = append(res.FlagReset, flag_api_error) + + if len(vouchersResp) == 0 { + res.FlagSet = append(res.FlagSet, flag_no_active_voucher) + return res, nil + } + + res.FlagReset = append(res.FlagReset, flag_no_active_voucher) + + // Check if user has an active voucher with proper error handling + activeSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) if err != nil { if db.IsNotFound(err) { - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - // Fetch vouchers from the API using the public key - vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_no_active_voucher) - return res, nil - } - - // Return if there is no voucher - if len(vouchersResp) == 0 { - res.FlagSet = append(res.FlagSet, flag_no_active_voucher) - return res, nil - } - - // Use only the first voucher + // No active voucher, set the first one as default firstVoucher := vouchersResp[0] defaultSym := firstVoucher.TokenSymbol defaultBal := firstVoucher.Balance @@ -1915,69 +2064,31 @@ func (h *MenuHandlers) SetDefaultVoucher(ctx context.Context, sym string, input // Scale down the balance scaledBalance := store.ScaleDownBalance(defaultBal, defaultDec) - // TODO: implement atomic transaction - // set the active symbol - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(defaultSym)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write defaultSym entry with", "key", storedb.DATA_ACTIVE_SYM, "value", defaultSym, "error", err) - return res, err - } - // set the active balance - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_BAL, []byte(scaledBalance)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", storedb.DATA_ACTIVE_BAL, "value", scaledBalance, "error", err) - return res, err - } - // set the active decimals - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_DECIMAL, []byte(defaultDec)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write defaultDec entry with", "key", storedb.DATA_ACTIVE_DECIMAL, "value", defaultDec, "error", err) - return res, err - } - // set the active contract address - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte(defaultAddr)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write defaultAddr entry with", "key", storedb.DATA_ACTIVE_ADDRESS, "value", defaultAddr, "error", err) - return res, err + firstVoucherMap := map[storedb.DataTyp]string{ + storedb.DATA_ACTIVE_SYM: defaultSym, + storedb.DATA_ACTIVE_BAL: scaledBalance, + storedb.DATA_ACTIVE_DECIMAL: defaultDec, + storedb.DATA_ACTIVE_ADDRESS: defaultAddr, } - return res, nil + for key, value := range firstVoucherMap { + if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { + logg.ErrorCtxf(ctx, "Failed to write active voucher data", "key", key, "error", err) + return res, err + } + err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write voucher db log entry", "key", key, "value", value) + } + } + + logg.InfoCtxf(ctx, "Default voucher set", "symbol", defaultSym, "balance", defaultBal, "decimals", defaultDec, "address", defaultAddr) + } else { + logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) + return res, err } - - logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err) - return res, err - } - - res.FlagReset = append(res.FlagReset, flag_no_active_voucher) - - return res, nil -} - -// CheckVouchers retrieves the token holdings from the API using the "PublicKey" and stores -// them to gdbm. -func (h *MenuHandlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { - var res resource.Result - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - - userStore := h.userdataStore - publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - // Fetch vouchers from the API using the public key - vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) - if err != nil { - return res, nil - } - - // check the current active sym and update the data - activeSym, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM) - if activeSym != nil { + } else { + // Update active voucher balance activeSymStr := string(activeSym) // Find the matching voucher data @@ -2007,9 +2118,8 @@ func (h *MenuHandlers) CheckVouchers(ctx context.Context, sym string, input []by } } - data := store.ProcessVouchers(vouchersResp) - // Store all voucher data + data := store.ProcessVouchers(vouchersResp) dataMap := map[storedb.DataTyp]string{ storedb.DATA_VOUCHER_SYMBOLS: data.Symbols, storedb.DATA_VOUCHER_BALANCES: data.Balances, @@ -2017,9 +2127,11 @@ func (h *MenuHandlers) CheckVouchers(ctx context.Context, sym string, input []by storedb.DATA_VOUCHER_ADDRESSES: data.Addresses, } + // Write data entries for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value)); err != nil { - return res, nil + if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { + logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err) + continue } } @@ -2029,16 +2141,25 @@ func (h *MenuHandlers) CheckVouchers(ctx context.Context, sym string, input []by // GetVoucherList fetches the list of vouchers and formats them. func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + userStore := h.userdataStore // Read vouchers from the store - voucherData, err := h.prefixDb.Get(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS)) + voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS) + logg.InfoCtxf(ctx, "reading GetVoucherList entries for sessionId: %s", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData) if err != nil { - logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err) + logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_SYMBOLS, "error", err) return res, err } formattedData := h.ReplaceSeparatorFunc(string(voucherData)) + logg.InfoCtxf(ctx, "final output for sessionId: %s", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "formattedData", formattedData) + res.Content = string(formattedData) return res, nil @@ -2065,7 +2186,7 @@ func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte return res, nil } - metadata, err := store.GetVoucherData(ctx, h.prefixDb, inputStr) + metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr) if err != nil { return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } @@ -2135,6 +2256,7 @@ func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input res.FlagSet = append(res.FlagSet, flag_api_error) return res, nil } + res.FlagReset = append(res.FlagReset, flag_api_error) res.Content = fmt.Sprintf( "Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation, @@ -2155,6 +2277,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input flag_api_error, _ := h.flagManager.GetFlag("flag_api_error") userStore := h.userdataStore + logdb := h.logDb publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) if err != nil { logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) @@ -2168,6 +2291,7 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) return res, err } + res.FlagReset = append(res.FlagReset, flag_api_error) // Return if there are no transactions if len(transactionsResp) == 0 { @@ -2194,6 +2318,10 @@ func (h *MenuHandlers) CheckTransactions(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) return res, err } + err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write tx db log entry", "key", key, "value", value) + } } res.FlagReset = append(res.FlagReset, flag_no_transfers) @@ -2394,6 +2522,121 @@ func (h *MenuHandlers) constructAccountAlias(ctx context.Context) error { return nil } +// RequestCustomAlias requests an ENS based alias name based on a user's input,then saves it as temporary value +func (h *MenuHandlers) RequestCustomAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + if string(input) == "0" { + return res, nil + } + + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + + store := h.userdataStore + aliasHint, err := store.ReadEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE) + if err != nil { + if db.IsNotFound(err) { + return res, nil + } + return res, err + } + //Ensures that the call doesn't happen twice for the same alias hint + if !bytes.Equal(aliasHint, input) { + err = store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(string(input))) + if err != nil { + return res, err + } + pubKey, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + return res, nil + } + } + sanitizedInput := sanitizeAliasHint(string(input)) + aliasResult, err := h.accountService.RequestAlias(ctx, string(pubKey), sanitizedInput) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + logg.ErrorCtxf(ctx, "failed to retrieve alias", "alias", string(aliasHint), "error_alias_request", err) + return res, nil + } + res.FlagReset = append(res.FlagReset, flag_api_error) + + alias := aliasResult.Alias + logg.InfoCtxf(ctx, "Suggested alias ", "alias", alias) + + //Store the returned alias,wait for user to confirm it as new account alias + err = store.WriteEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS, []byte(alias)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write account alias", "key", storedb.DATA_TEMPORARY_VALUE, "value", alias, "error", err) + return res, err + } + } + return res, nil +} + +func sanitizeAliasHint(input string) string { + for i, r := range input { + // Check if the character is a special character (non-alphanumeric) + if !unicode.IsLetter(r) && !unicode.IsNumber(r) { + return input[:i] + } + } + // If no special character is found, return the whole input + return input +} + +// GetSuggestedAlias loads and displays the suggested alias name from the temporary value +func (h *MenuHandlers) GetSuggestedAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + suggestedAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS) + if err != nil { + return res, nil + } + res.Content = string(suggestedAlias) + return res, nil +} + +// ConfirmNewAlias reads the suggested alias from the [DATA_SUGGECTED_ALIAS] key and confirms it as the new account alias. +func (h *MenuHandlers) ConfirmNewAlias(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + store := h.userdataStore + logdb := h.logDb + + flag_alias_set, _ := h.flagManager.GetFlag("flag_alias_set") + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + newAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_SUGGESTED_ALIAS) + if err != nil { + return res, nil + } + logg.InfoCtxf(ctx, "Confirming new alias", "alias", string(newAlias)) + err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(string(newAlias))) + if err != nil { + logg.ErrorCtxf(ctx, "failed to clear DATA_ACCOUNT_ALIAS_VALUE entry with", "key", storedb.DATA_ACCOUNT_ALIAS, "value", "empty", "error", err) + return res, err + } + + err = logdb.WriteLogEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS, []byte(newAlias)) + if err != nil { + logg.DebugCtxf(ctx, "Failed to write account alias db log entry", "key", storedb.DATA_ACCOUNT_ALIAS, "value", newAlias) + } + + res.FlagSet = append(res.FlagSet, flag_alias_set) + return res, nil +} + // ClearTemporaryValue empties the DATA_TEMPORARY_VALUE at the main menu to prevent // previously stored data from being accessed func (h *MenuHandlers) ClearTemporaryValue(ctx context.Context, sym string, input []byte) (resource.Result, error) { diff --git a/handlers/application/menuhandler_test.go b/handlers/application/menuhandler_test.go index f008085..d07e0cc 100644 --- a/handlers/application/menuhandler_test.go +++ b/handlers/application/menuhandler_test.go @@ -62,6 +62,25 @@ func InitializeTestStore(t *testing.T) (context.Context, *store.UserDataStore) { return ctx, store } +// InitializeTestLogdbStore sets up and returns an in-memory database and logdb store. +func InitializeTestLogdbStore(t *testing.T) (context.Context, *store.UserDataStore) { + ctx := context.Background() + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err, "Failed to connect to memDb") + + // Create UserDataStore with memDb + logdb := &store.UserDataStore{Db: db} + + t.Cleanup(func() { + db.Close(ctx) // Ensure the DB is closed after each test + }) + + return ctx, logdb +} + func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storedb.SubPrefixDb { db := memdb.NewMemDb() err := db.Connect(ctx, "") @@ -76,6 +95,7 @@ func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storedb.SubPr func TestNewMenuHandlers(t *testing.T) { _, store := InitializeTestStore(t) + _, logdb := InitializeTestLogdbStore(t) fm, err := NewFlagManager(flagsPath) if err != nil { @@ -86,7 +106,7 @@ func TestNewMenuHandlers(t *testing.T) { // Test case for valid UserDataStore t.Run("Valid UserDataStore", func(t *testing.T) { - handlers, err := NewMenuHandlers(fm, store, &accountService, mockReplaceSeparator) + handlers, err := NewMenuHandlers(fm, store, logdb, &accountService, mockReplaceSeparator) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -110,7 +130,7 @@ func TestNewMenuHandlers(t *testing.T) { // Test case for nil UserDataStore t.Run("Nil UserDataStore", func(t *testing.T) { - handlers, err := NewMenuHandlers(fm, nil, &accountService, mockReplaceSeparator) + handlers, err := NewMenuHandlers(fm, nil, logdb, &accountService, mockReplaceSeparator) if err == nil { t.Fatal("expected an error, got none") } @@ -192,8 +212,13 @@ func TestInit(t *testing.T) { func TestCreateAccount(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, err := NewFlagManager(flagsPath) if err != nil { @@ -201,6 +226,8 @@ func TestCreateAccount(t *testing.T) { } flag_account_created, err := fm.GetFlag("flag_account_created") + flag_account_creation_failed, _ := fm.GetFlag("flag_account_creation_failed") + if err != nil { t.Logf(err.Error()) } @@ -217,7 +244,8 @@ func TestCreateAccount(t *testing.T) { PublicKey: "0xD3adB33f", }, expectedResult: resource.Result{ - FlagSet: []uint32{flag_account_created}, + FlagSet: []uint32{flag_account_created}, + FlagReset: []uint32{flag_account_creation_failed}, }, }, } @@ -226,8 +254,9 @@ func TestCreateAccount(t *testing.T) { mockAccountService := new(mocks.MockAccountService) h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, accountService: mockAccountService, + logDb: logDb, flagManager: fm, } @@ -265,8 +294,13 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { func TestSaveFirstname(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -282,7 +316,7 @@ func TestSaveFirstname(t *testing.T) { // Define test data firstName := "John" - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { t.Fatal(err) } @@ -290,9 +324,10 @@ func TestSaveFirstname(t *testing.T) { // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, flagManager: fm, st: mockState, + logDb: logDb, } // Call the method @@ -303,14 +338,19 @@ func TestSaveFirstname(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_FIRST_NAME entry has been updated with the temporary value - storedFirstName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_FIRST_NAME) + storedFirstName, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_FIRST_NAME) assert.Equal(t, firstName, string(storedFirstName)) } func TestSaveFamilyname(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -328,15 +368,16 @@ func TestSaveFamilyname(t *testing.T) { // Define test data familyName := "Doeee" - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { t.Fatal(err) } // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, st: mockState, flagManager: fm, + logDb: logDb, } // Call the method @@ -347,14 +388,19 @@ func TestSaveFamilyname(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value - storedFamilyName, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME) + storedFamilyName, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_FAMILY_NAME) assert.Equal(t, familyName, string(storedFamilyName)) } func TestSaveYoB(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -370,7 +416,7 @@ func TestSaveYoB(t *testing.T) { // Define test data yob := "1980" - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { t.Fatal(err) } @@ -378,9 +424,10 @@ func TestSaveYoB(t *testing.T) { // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, flagManager: fm, st: mockState, + logDb: logDb, } // Call the method @@ -391,14 +438,19 @@ func TestSaveYoB(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_YOB entry has been updated with the temporary value - storedYob, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_YOB) + storedYob, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_YOB) assert.Equal(t, yob, string(storedYob)) } func TestSaveLocation(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -414,7 +466,7 @@ func TestSaveLocation(t *testing.T) { // Define test data location := "Kilifi" - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { t.Fatal(err) } @@ -422,9 +474,10 @@ func TestSaveLocation(t *testing.T) { // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, flagManager: fm, st: mockState, + logDb: logDb, } // Call the method @@ -435,14 +488,19 @@ func TestSaveLocation(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_LOCATION entry has been updated with the temporary value - storedLocation, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_LOCATION) + storedLocation, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_LOCATION) assert.Equal(t, location, string(storedLocation)) } func TestSaveOfferings(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -458,7 +516,7 @@ func TestSaveOfferings(t *testing.T) { // Define test data offerings := "Bananas" - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { t.Fatal(err) } @@ -466,9 +524,10 @@ func TestSaveOfferings(t *testing.T) { // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, flagManager: fm, st: mockState, + logDb: logDb, } // Call the method @@ -479,14 +538,19 @@ func TestSaveOfferings(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_OFFERINGS entry has been updated with the temporary value - storedOfferings, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS) + storedOfferings, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_OFFERINGS) assert.Equal(t, offerings, string(storedOfferings)) } func TestSaveGender(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, _ := NewFlagManager(flagsPath) @@ -526,16 +590,17 @@ func TestSaveGender(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := store.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { + if err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { t.Fatal(err) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, st: mockState, flagManager: fm, + logDb: logDb, } expectedResult := resource.Result{} @@ -550,7 +615,7 @@ func TestSaveGender(t *testing.T) { assert.Equal(t, expectedResult, res) // Verify that the DATA_GENDER entry has been updated with the temporary value - storedGender, _ := store.ReadEntry(ctx, sessionId, storedb.DATA_GENDER) + storedGender, _ := userStore.ReadEntry(ctx, sessionId, storedb.DATA_GENDER) assert.Equal(t, tt.expectedGender, string(storedGender)) }) } @@ -558,20 +623,27 @@ func TestSaveGender(t *testing.T) { func TestSaveTemporaryPin(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + + ctx, userdatastore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + logDb := store.LogDb{ + Db: logdb, + } + fm, err := NewFlagManager(flagsPath) if err != nil { log.Fatal(err) } - flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") + flag_invalid_pin, _ := fm.GetFlag("flag_invalid_pin") // Create the MenuHandlers instance with the mock flag manager h := &MenuHandlers{ flagManager: fm, - userdataStore: store, + userdataStore: userdatastore, + logDb: logDb, } // Define test cases @@ -584,14 +656,14 @@ func TestSaveTemporaryPin(t *testing.T) { name: "Valid Pin entry", input: []byte("1234"), expectedResult: resource.Result{ - FlagReset: []uint32{flag_incorrect_pin}, + FlagReset: []uint32{flag_invalid_pin}, }, }, { name: "Invalid Pin entry", input: []byte("12343"), expectedResult: resource.Result{ - FlagSet: []uint32{flag_incorrect_pin}, + FlagSet: []uint32{flag_invalid_pin}, }, }, } @@ -612,9 +684,15 @@ func TestSaveTemporaryPin(t *testing.T) { func TestCheckIdentifier(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + ctx, userdatastore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } + // Define test cases tests := []struct { name string @@ -634,14 +712,15 @@ func TestCheckIdentifier(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(tt.publicKey)) + err := userdatastore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(tt.publicKey)) if err != nil { t.Fatal(err) } // Create the MenuHandlers instance with the mock store h := &MenuHandlers{ - userdataStore: store, + userdataStore: userdatastore, + logDb: logDb, } // Call the method @@ -1037,6 +1116,7 @@ func TestAuthorize(t *testing.T) { flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_invalid_pin, _ := fm.GetFlag("flag_invalid_pin") // Set 1234 is the correct account pin accountPIN := "1234" @@ -1070,9 +1150,11 @@ func TestAuthorize(t *testing.T) { }, }, { - name: "Test with pin that is not a 4 digit", - input: []byte("1235aqds"), - expectedResult: resource.Result{}, + name: "Test with pin that is not a 4 digit", + input: []byte("1235aqds"), + expectedResult: resource.Result{ + FlagSet: []uint32{flag_invalid_pin}, + }, }, } @@ -1634,40 +1716,64 @@ func TestValidateRecipient(t *testing.T) { // Define test cases tests := []struct { - name string - input []byte - expectedResult resource.Result + name string + input []byte + expectError bool + expectedRecipient []byte + expectedResult resource.Result }{ { - name: "Test with invalid recepient", - input: []byte("7?1234"), + name: "Test with invalid recepient", + input: []byte("7?1234"), + expectError: true, expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_recipient}, Content: "7?1234", }, }, { - name: "Test with valid unregistered recepient", - input: []byte("0712345678"), + name: "Test with valid unregistered recepient", + input: []byte("0712345678"), + expectError: true, expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_recipient_with_invite}, Content: "0712345678", }, }, { - name: "Test with valid registered recepient", - input: []byte("0711223344"), - expectedResult: resource.Result{}, + name: "Test with valid registered recepient", + input: []byte("0711223344"), + expectError: false, + expectedRecipient: []byte(publicKey), + expectedResult: resource.Result{}, }, { - name: "Test with address", - input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), - expectedResult: resource.Result{}, + name: "Test with address", + input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + expectError: false, + expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + expectedResult: resource.Result{}, }, { - name: "Test with alias recepient", - input: []byte("alias123.sarafu.local"), - expectedResult: resource.Result{}, + name: "Test with alias recepient", + input: []byte("alias123.sarafu.local"), + expectError: false, + expectedRecipient: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + expectedResult: resource.Result{}, + }, + { + name: "Test for checksummed address", + input: []byte("0x5523058cdffe5f3c1eadadd5015e55c6e00fb439"), + expectError: false, + expectedRecipient: []byte("0x5523058cdFfe5F3c1EaDADD5015E55C6E00fb439"), + expectedResult: resource.Result{}, + }, + { + name: "Test with valid registered recepient that has white spaces", + input: []byte("0711 22 33 44"), + expectError: false, + expectedRecipient: []byte(publicKey), + expectedResult: resource.Result{}, }, } @@ -1700,6 +1806,12 @@ func TestValidateRecipient(t *testing.T) { t.Error(err) } + if !tt.expectError { + storedRecipientAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_RECIPIENT) + assert.NoError(t, err) + assert.Equal(t, tt.expectedRecipient, storedRecipientAddress) + } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") }) @@ -1840,53 +1952,7 @@ func TestGetProfile(t *testing.T) { } } -func TestVerifyNewPin(t *testing.T) { - sessionId := "session123" - - fm, _ := NewFlagManager(flagsPath) - mockState := state.NewState(16) - - flag_valid_pin, _ := fm.GetFlag("flag_valid_pin") - mockAccountService := new(mocks.MockAccountService) - h := &MenuHandlers{ - flagManager: fm, - accountService: mockAccountService, - st: mockState, - } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - tests := []struct { - name string - input []byte - expectedResult resource.Result - }{ - { - name: "Test with valid pin", - input: []byte("1234"), - expectedResult: resource.Result{ - FlagSet: []uint32{flag_valid_pin}, - }, - }, - { - name: "Test with invalid pin", - input: []byte("123"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_valid_pin}, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - //Call the function under test - res, _ := h.VerifyNewPin(ctx, "verify_new_pin", tt.input) - - //Assert that the result set to content is what was expected - assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") - }) - } -} - -func TestConfirmPin(t *testing.T) { +func TestConfirmPinChange(t *testing.T) { sessionId := "session123" mockState := state.NewState(16) @@ -1895,6 +1961,8 @@ func TestConfirmPin(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_pin_mismatch, _ := fm.GetFlag("flag_pin_mismatch") + flag_account_pin_reset, _ := fm.GetFlag("flag_account_pin_reset") + mockAccountService := new(mocks.MockAccountService) h := &MenuHandlers{ userdataStore: store, @@ -1914,7 +1982,7 @@ func TestConfirmPin(t *testing.T) { input: []byte("1234"), temporarypin: "1234", expectedResult: resource.Result{ - FlagReset: []uint32{flag_pin_mismatch}, + FlagReset: []uint32{flag_pin_mismatch, flag_account_pin_reset}, }, }, } @@ -1987,36 +2055,57 @@ func TestFetchCommunityBalance(t *testing.T) { } } -func TestSetDefaultVoucher(t *testing.T) { +func TestManageVouchers(t *testing.T) { sessionId := "session123" - ctx, store := InitializeTestStore(t) + publicKey := "0X13242618721" + + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } fm, err := NewFlagManager(flagsPath) if err != nil { - t.Logf(err.Error()) + t.Fatal(err) } flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher") if err != nil { - t.Logf(err.Error()) + t.Fatal(err) + } + flag_api_error, err := fm.GetFlag("flag_api_call_error") + + if err != nil { + t.Fatal(err) } - publicKey := "0X13242618721" + err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } tests := []struct { - name string - vouchersResp []dataserviceapi.TokenHoldings - expectedResult resource.Result + name string + vouchersResp []dataserviceapi.TokenHoldings + storedActiveVoucher string + expectedVoucherSymbols []byte + expectedUpdatedAddress []byte + expectedResult resource.Result }{ { - name: "Test no vouchers available", - vouchersResp: []dataserviceapi.TokenHoldings{}, + name: "No vouchers available", + vouchersResp: []dataserviceapi.TokenHoldings{}, + expectedVoucherSymbols: []byte(""), + expectedUpdatedAddress: []byte(""), expectedResult: resource.Result{ - FlagSet: []uint32{flag_no_active_voucher}, + FlagSet: []uint32{flag_no_active_voucher}, + FlagReset: []uint32{flag_api_error}, }, }, { - name: "Test set default voucher when no active voucher is set", + name: "Set default voucher when no active voucher is set", vouchersResp: []dataserviceapi.TokenHoldings{ { ContractAddress: "0x123", @@ -2025,7 +2114,24 @@ func TestSetDefaultVoucher(t *testing.T) { Balance: "100", }, }, - expectedResult: resource.Result{}, + expectedVoucherSymbols: []byte("1:TOKEN1"), + expectedUpdatedAddress: []byte("0x123"), + expectedResult: resource.Result{ + FlagReset: []uint32{flag_api_error, flag_no_active_voucher}, + }, + }, + { + name: "Check and update active voucher balance", + vouchersResp: []dataserviceapi.TokenHoldings{ + {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, + {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, + }, + storedActiveVoucher: "SRF", + expectedVoucherSymbols: []byte("1:SRF\n2:MILO"), + expectedUpdatedAddress: []byte("0xd4c288865Ce"), + expectedResult: resource.Result{ + FlagReset: []uint32{flag_api_error, flag_no_active_voucher}, + }, }, } @@ -2034,109 +2140,63 @@ func TestSetDefaultVoucher(t *testing.T) { mockAccountService := new(mocks.MockAccountService) h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, accountService: mockAccountService, flagManager: fm, - } - - err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) - if err != nil { - t.Fatal(err) + logDb: logDb, } mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) - res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input")) + // Store active voucher if needed + if tt.storedActiveVoucher != "" { + err := userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte(tt.storedActiveVoucher)) + if err != nil { + t.Fatal(err) + } + err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte("0x41c188D45rfg6ds")) + if err != nil { + t.Fatal(err) + } + } + res, err := h.ManageVouchers(ctx, "manage_vouchers", []byte("")) assert.NoError(t, err) + assert.Equal(t, tt.expectedResult, res) - assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") + if tt.storedActiveVoucher != "" { + // Validate stored voucher symbols + voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS) + assert.NoError(t, err) + assert.Equal(t, tt.expectedVoucherSymbols, voucherData) - mockAccountService.AssertExpectations(t) + // Validate stored active contract address + updatedAddress, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS) + assert.NoError(t, err) + assert.Equal(t, tt.expectedUpdatedAddress, updatedAddress) + + mockAccountService.AssertExpectations(t) + } }) } } -func TestCheckVouchers(t *testing.T) { - mockAccountService := new(mocks.MockAccountService) - sessionId := "session123" - publicKey := "0X13242618721" - - ctx, store := InitializeTestStore(t) - ctx = context.WithValue(ctx, "SessionId", sessionId) - spdb := InitializeTestSubPrefixDb(t, ctx) - - h := &MenuHandlers{ - userdataStore: store, - accountService: mockAccountService, - prefixDb: spdb, - } - - err := store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) - if err != nil { - t.Fatal(err) - } - - mockVouchersResponse := []dataserviceapi.TokenHoldings{ - {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, - {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, - } - - // store the default voucher data - err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM, []byte("SRF")) - if err != nil { - t.Fatal(err) - } - err = store.WriteEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS, []byte("0x41c188D45rfg6ds")) - if err != nil { - t.Fatal(err) - } - - expectedSym := []byte("1:SRF\n2:MILO") - expectedUpdatedAddress := []byte("0xd4c288865Ce") - - mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil) - - _, err = h.CheckVouchers(ctx, "check_vouchers", []byte("")) - assert.NoError(t, err) - - // Read voucher sym data from the store - voucherData, err := spdb.Get(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS)) - if err != nil { - t.Fatal(err) - } - - // Read active contract address from the store - updatedAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS) - if err != nil { - t.Fatal(err) - } - - // assert that the data is stored correctly - assert.Equal(t, expectedSym, voucherData) - // assert that the address is updated - assert.Equal(t, expectedUpdatedAddress, updatedAddress) - - mockAccountService.AssertExpectations(t) -} - func TestGetVoucherList(t *testing.T) { sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - spdb := InitializeTestSubPrefixDb(t, ctx) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) // Initialize MenuHandlers h := &MenuHandlers{ - prefixDb: spdb, + userdataStore: store, ReplaceSeparatorFunc: mockReplaceSeparator, } mockSyms := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err := spdb.Put(ctx, storedb.ToBytes(storedb.DATA_VOUCHER_SYMBOLS), mockSyms) + err := store.WriteEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS, mockSyms) if err != nil { t.Fatal(err) } @@ -2156,15 +2216,11 @@ func TestViewVoucher(t *testing.T) { } ctx, store := InitializeTestStore(t) sessionId := "session123" - ctx = context.WithValue(ctx, "SessionId", sessionId) - spdb := InitializeTestSubPrefixDb(t, ctx) - h := &MenuHandlers{ userdataStore: store, flagManager: fm, - prefixDb: spdb, } // Define mock voucher data @@ -2177,7 +2233,7 @@ func TestViewVoucher(t *testing.T) { // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value)) + err := store.WriteEntry(ctx, sessionId, key, []byte(value)) if err != nil { t.Fatal(err) } @@ -2226,6 +2282,8 @@ func TestGetVoucherDetails(t *testing.T) { if err != nil { t.Logf(err.Error()) } + + flag_api_error, _ := fm.GetFlag("flag_api_call_error") mockAccountService := new(mocks.MockAccountService) sessionId := "session123" @@ -2253,8 +2311,8 @@ func TestGetVoucherDetails(t *testing.T) { "Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", tokenDetails.TokenName, tokenDetails.TokenSymbol, tokenDetails.TokenCommodity, tokenDetails.TokenLocation, ) mockAccountService.On("VoucherData", string(tokA_AAddress)).Return(tokenDetails, nil) - res, err := h.GetVoucherDetails(ctx, "SessionId", []byte("")) + expectedResult.FlagReset = append(expectedResult.FlagReset, flag_api_error) assert.NoError(t, err) assert.Equal(t, expectedResult, res) } @@ -2357,10 +2415,8 @@ func TestCheckBlockedStatus(t *testing.T) { if err != nil { t.Logf(err.Error()) } - flag_account_blocked, err := fm.GetFlag("flag_account_blocked") - if err != nil { - t.Logf(err.Error()) - } + flag_account_blocked, _ := fm.GetFlag("flag_account_blocked") + flag_account_pin_reset, _ := fm.GetFlag("flag_account_pin_reset") h := &MenuHandlers{ userdataStore: store, @@ -2375,13 +2431,15 @@ func TestCheckBlockedStatus(t *testing.T) { { name: "Currently blocked account", currentWrongPinAttempts: "4", - expectedResult: resource.Result{}, + expectedResult: resource.Result{ + FlagReset: []uint32{flag_account_pin_reset}, + }, }, { name: "Account with 0 wrong PIN attempts", currentWrongPinAttempts: "0", expectedResult: resource.Result{ - FlagReset: []uint32{flag_account_blocked}, + FlagReset: []uint32{flag_account_pin_reset, flag_account_blocked}, }, }, } @@ -2442,8 +2500,14 @@ func TestCheckTransactions(t *testing.T) { sessionId := "session123" publicKey := "0X13242618721" - ctx, store := InitializeTestStore(t) + ctx, userStore := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) + _, logdb := InitializeTestLogdbStore(t) + + logDb := store.LogDb{ + Db: logdb, + } + spdb := InitializeTestSubPrefixDb(t, ctx) fm, err := NewFlagManager(flagsPath) @@ -2452,13 +2516,14 @@ func TestCheckTransactions(t *testing.T) { } h := &MenuHandlers{ - userdataStore: store, + userdataStore: userStore, accountService: mockAccountService, prefixDb: spdb, + logDb: logDb, flagManager: fm, } - err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) + err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) if err != nil { t.Fatal(err) } @@ -2910,173 +2975,6 @@ func TestValidateBlockedNumber(t *testing.T) { } } -func TestSaveOthersTemporaryPin(t *testing.T) { - sessionId := "session123" - blockedNumber := "+254712345678" - testPin := "1234" - - ctx, userStore := InitializeTestStore(t) - ctx = context.WithValue(ctx, "SessionId", sessionId) - - h := &MenuHandlers{ - userdataStore: userStore, - } - - tests := []struct { - name string - sessionId string - blockedNumber string - testPin string - setup func() error // Setup function for each test case - expectedError bool - verifyResult func(t *testing.T) // Function to verify the result - }{ - { - name: "Missing Session ID", - sessionId: "", // Empty session ID - blockedNumber: blockedNumber, - testPin: testPin, - setup: nil, - expectedError: true, - verifyResult: nil, - }, - { - name: "Failed to Read Blocked Number", - sessionId: sessionId, - blockedNumber: blockedNumber, - testPin: testPin, - setup: func() error { - // Do not write the blocked number to simulate a read failure - return nil - }, - expectedError: true, - verifyResult: nil, - }, - - { - name: "Successfully save hashed PIN", - sessionId: sessionId, - blockedNumber: blockedNumber, - testPin: testPin, - setup: func() error { - // Write the blocked number to the store - return userStore.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) - }, - expectedError: false, - verifyResult: func(t *testing.T) { - // Read the stored hashed PIN - othersHashedPin, err := userStore.ReadEntry(ctx, blockedNumber, storedb.DATA_TEMPORARY_VALUE) - if err != nil { - t.Fatal(err) - } - - // Verify that the stored hashed PIN matches the original PIN - if !pin.VerifyPIN(string(othersHashedPin), testPin) { - t.Fatal("stored hashed PIN does not match the original PIN") - } - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set up the context with the session ID - ctx := context.WithValue(context.Background(), "SessionId", tt.sessionId) - - // Run the setup function if provided - if tt.setup != nil { - err := tt.setup() - if err != nil { - t.Fatal(err) - } - } - - // Call the function under test - _, err := h.SaveOthersTemporaryPin(ctx, "save_others_temporary_pin", []byte(tt.testPin)) - - // Assert the error - if tt.expectedError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - // Verify the result if a verification function is provided - if tt.verifyResult != nil { - tt.verifyResult(t) - } - }) - } -} - -func TestCheckBlockedNumPinMisMatch(t *testing.T) { - sessionId := "session123" - blockedNumber := "+254712345678" - testPin := "1234" - mockState := state.NewState(128) - - ctx, userStore := InitializeTestStore(t) - ctx = context.WithValue(ctx, "SessionId", sessionId) - - hashedPIN, err := pin.HashPIN(testPin) - if err != nil { - logg.ErrorCtxf(ctx, "failed to hash testPin", "error", err) - t.Fatal(err) - } - - fm, err := NewFlagManager(flagsPath) - if err != nil { - t.Fatal(err) - } - flag_pin_mismatch, _ := fm.GetFlag("flag_pin_mismatch") - - h := &MenuHandlers{ - userdataStore: userStore, - st: mockState, - flagManager: fm, - } - - // Write initial data to the store - err = userStore.WriteEntry(ctx, sessionId, storedb.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) - if err != nil { - t.Fatal(err) - } - err = userStore.WriteEntry(ctx, blockedNumber, storedb.DATA_TEMPORARY_VALUE, []byte(hashedPIN)) - if err != nil { - t.Fatal(err) - } - - tests := []struct { - name string - input []byte - expectedResult resource.Result - }{ - { - name: "Successful PIN match", - input: []byte(testPin), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_pin_mismatch}, - }, - }, - { - name: "PIN mismatch", - input: []byte("1345"), - expectedResult: resource.Result{ - FlagSet: []uint32{flag_pin_mismatch}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - res, err := h.CheckBlockedNumPinMisMatch(ctx, "sym", tt.input) - - assert.NoError(t, err) - assert.Equal(t, tt.expectedResult, res) - }) - } -} - func TestGetCurrentProfileInfo(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) @@ -3233,30 +3131,6 @@ func TestResetOthersPin(t *testing.T) { assert.NoError(t, err) } -func TestResetValidPin(t *testing.T) { - ctx := context.Background() - - fm, err := NewFlagManager(flagsPath) - if err != nil { - t.Fatal(err) - } - flag_valid_pin, _ := fm.GetFlag("flag_valid_pin") - - expectedResult := resource.Result{ - FlagReset: []uint32{flag_valid_pin}, - } - - h := &MenuHandlers{ - flagManager: fm, - } - - res, err := h.ResetValidPin(ctx, "reset_valid_pin", []byte("")) - - assert.NoError(t, err) - - assert.Equal(t, expectedResult, res) -} - func TestResetUnregisteredNumber(t *testing.T) { ctx := context.Background() @@ -3473,15 +3347,6 @@ func TestUpdateAllProfileItems(t *testing.T) { err = store.WriteEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY, []byte(publicKey)) require.NoError(t, err) - aliasInput := fmt.Sprintf("%s%s", profileItems[0], profileItems[1]) - - // Mock the account alias response - mockAccountService.On( - "RequestAlias", - publicKey, - aliasInput, - ).Return(&models.RequestAliasResult{Alias: "JohnDoe"}, nil) - // Call the function under test res, err := h.UpdateAllProfileItems(ctx, "symbol", nil) assert.NoError(t, err) @@ -3493,10 +3358,6 @@ func TestUpdateAllProfileItems(t *testing.T) { assert.Equal(t, profileItems[i], string(storedValue)) } - // Validate alias storage - storedAlias, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACCOUNT_ALIAS) - assert.NoError(t, err) - assert.Equal(t, "JohnDoe", string(storedAlias)) assert.Equal(t, expectedResult, res) } diff --git a/handlers/event/token.go b/handlers/event/token.go index 713dab7..b9edb8b 100644 --- a/handlers/event/token.go +++ b/handlers/event/token.go @@ -49,9 +49,8 @@ func (eu *EventsUpdater) updateToken(ctx context.Context, identity identity.Iden // set default token to given symbol. func (eu *EventsUpdater) updateDefaultToken(ctx context.Context, identity identity.Identity, userStore *store.UserDataStore, activeSym string) error { - pfxDb := toPrefixDb(userStore, identity.SessionId) // TODO: the activeSym input should instead be newline separated list? - tokenData, err := store.GetVoucherData(ctx, pfxDb, activeSym) + tokenData, err := store.GetVoucherData(ctx, userStore, identity.SessionId, activeSym) if err != nil { return err } diff --git a/handlers/local.go b/handlers/local.go index 50d6489..cf6c63f 100644 --- a/handlers/local.go +++ b/handlers/local.go @@ -27,6 +27,7 @@ type LocalHandlerService struct { DbRs *resource.DbResource Pe *persist.Persister UserdataStore *db.Db + LogDb *db.Db Cfg engine.Config Rs resource.Resource first resource.EntryFunc @@ -57,12 +58,16 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } +func (ls *LocalHandlerService) SetLogDb(db *db.Db) { + ls.LogDb = db +} + func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) (*application.MenuHandlers, error) { replaceSeparatorFunc := func(input string) string { return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator) } - appHandlers, err := application.NewMenuHandlers(ls.Parser, *ls.UserdataStore, accountService, replaceSeparatorFunc) + appHandlers, err := application.NewMenuHandlers(ls.Parser, *ls.UserdataStore, *ls.LogDb, accountService, replaceSeparatorFunc) if err != nil { return nil, err } @@ -99,23 +104,18 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) ls.DbRs.AddLocalFunc("verify_yob", appHandlers.VerifyYob) ls.DbRs.AddLocalFunc("reset_incorrect_date_format", appHandlers.ResetIncorrectYob) ls.DbRs.AddLocalFunc("initiate_transaction", appHandlers.InitiateTransaction) - ls.DbRs.AddLocalFunc("verify_new_pin", appHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", appHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", appHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("fetch_community_balance", appHandlers.FetchCommunityBalance) - ls.DbRs.AddLocalFunc("set_default_voucher", appHandlers.SetDefaultVoucher) - ls.DbRs.AddLocalFunc("check_vouchers", appHandlers.CheckVouchers) + ls.DbRs.AddLocalFunc("manage_vouchers", appHandlers.ManageVouchers) ls.DbRs.AddLocalFunc("get_vouchers", appHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", appHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("set_voucher", appHandlers.SetVoucher) ls.DbRs.AddLocalFunc("get_voucher_details", appHandlers.GetVoucherDetails) - ls.DbRs.AddLocalFunc("reset_valid_pin", appHandlers.ResetValidPin) - ls.DbRs.AddLocalFunc("check_pin_mismatch", appHandlers.CheckBlockedNumPinMisMatch) ls.DbRs.AddLocalFunc("validate_blocked_number", appHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", appHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", appHandlers.ResetUnregisteredNumber) ls.DbRs.AddLocalFunc("reset_others_pin", appHandlers.ResetOthersPin) - ls.DbRs.AddLocalFunc("save_others_temporary_pin", appHandlers.SaveOthersTemporaryPin) ls.DbRs.AddLocalFunc("get_current_profile_info", appHandlers.GetCurrentProfileInfo) ls.DbRs.AddLocalFunc("check_transactions", appHandlers.CheckTransactions) ls.DbRs.AddLocalFunc("get_transactions", appHandlers.GetTransactionsList) @@ -124,6 +124,13 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountService) ls.DbRs.AddLocalFunc("set_back", appHandlers.SetBack) ls.DbRs.AddLocalFunc("show_blocked_account", appHandlers.ShowBlockedAccount) ls.DbRs.AddLocalFunc("clear_temporary_value", appHandlers.ClearTemporaryValue) + ls.DbRs.AddLocalFunc("reset_invalid_pin", appHandlers.ResetInvalidPIN) + ls.DbRs.AddLocalFunc("request_custom_alias", appHandlers.RequestCustomAlias) + ls.DbRs.AddLocalFunc("get_suggested_alias", appHandlers.GetSuggestedAlias) + ls.DbRs.AddLocalFunc("confirm_new_alias", appHandlers.ConfirmNewAlias) + ls.DbRs.AddLocalFunc("check_account_created", appHandlers.CheckAccountCreated) + ls.DbRs.AddLocalFunc("reset_api_call_failure", appHandlers.ResetApiCallFailure) + ls.DbRs.AddLocalFunc("get_pools", appHandlers.GetPools) ls.DbRs.AddLocalFunc("swap_from_list", appHandlers.LoadSwapFromList) ls.DbRs.AddLocalFunc("swap_to_list", appHandlers.LoadSwapToList) diff --git a/internal/sms/sms.go b/internal/sms/sms.go new file mode 100644 index 0000000..f6d51ce --- /dev/null +++ b/internal/sms/sms.go @@ -0,0 +1,96 @@ +package sms + +import ( + "context" + "fmt" + + "git.defalsify.org/vise.git/logging" + "git.grassecon.net/grassrootseconomics/common/phone" + "git.grassecon.net/grassrootseconomics/sarafu-api/remote" + "git.grassecon.net/grassrootseconomics/sarafu-vise/store" + storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" +) + +var ( + logg = logging.NewVanilla().WithDomain("smsservice") +) + +type SmsService struct { + Accountservice remote.AccountService + Userdatastore store.UserDataStore +} + +// SendUpsellSMS will send an invitation SMS to an unregistered phone number +func (smsservice *SmsService) SendUpsellSMS(ctx context.Context, inviterPhone, inviteePhone string) error { + if !phone.IsValidPhoneNumber(inviterPhone) { + return fmt.Errorf("invalid inviter phone number %v", inviterPhone) + } + + if !phone.IsValidPhoneNumber(inviteePhone) { + return fmt.Errorf("Invalid invitee phone number %v", inviteePhone) + } + _, err := smsservice.Accountservice.SendUpsellSMS(ctx, inviterPhone, inviteePhone) + if err != nil { + return fmt.Errorf("Failed to send upsell sms: %v", err) + } + return nil +} + +// sendPINResetSMS will send an SMS to a user's phonenumber in the event that the associated account's PIN has been reset. +func (smsService *SmsService) SendPINResetSMS(ctx context.Context, adminPhoneNumber, blockedPhoneNumber string) error { + formattedAdminPhone, err := phone.FormatPhoneNumber(adminPhoneNumber) + if err != nil { + return fmt.Errorf("failed to format admin phone number: %w", err) + } + + formattedBlockedPhone, err := phone.FormatPhoneNumber(blockedPhoneNumber) + if err != nil { + return fmt.Errorf("failed to format blocked phone number: %w", err) + } + + if !phone.IsValidPhoneNumber(formattedAdminPhone) { + return fmt.Errorf("invalid admin phone number") + } + if !phone.IsValidPhoneNumber(formattedBlockedPhone) { + return fmt.Errorf("invalid blocked phone number") + } + + err = smsService.Accountservice.SendPINResetSMS(ctx, formattedAdminPhone, formattedBlockedPhone) + if err != nil { + return fmt.Errorf("failed to send pin reset sms: %v", err) + } + + return nil +} + +// SendAddressSMS will triger an SMS when a user navigates to the my address node.The SMS will be sent to the associated phonenumber. +func (smsService *SmsService) SendAddressSMS(ctx context.Context) error { + store := smsService.Userdatastore + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return fmt.Errorf("missing session") + } + + publicKey, err := store.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", storedb.DATA_PUBLIC_KEY, "error", err) + return err + } + + originPhone, err := phone.FormatPhoneNumber(sessionId) + if err != nil { + logg.DebugCtxf(ctx, "Failed to format origin phonenumber", "sessionid", sessionId) + return nil + } + + if !phone.IsValidPhoneNumber(originPhone) { + logg.InfoCtxf(ctx, "Invalid origin phone number", "origin phonenumber", originPhone) + return fmt.Errorf("invalid origin phone number") + } + err = smsService.Accountservice.SendAddressSMS(ctx, string(publicKey), originPhone) + if err != nil { + logg.DebugCtxf(ctx, "Failed to send address sms", "error", err) + return fmt.Errorf("Failed to send address sms: %v", err) + } + return nil +} diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index f4bc2df..38c382e 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -71,7 +71,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "5", @@ -108,11 +108,11 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "2", @@ -149,7 +149,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "5", @@ -173,7 +173,7 @@ }, { "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "0", @@ -190,7 +190,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "5", @@ -202,23 +202,7 @@ }, { "input": "0700000000", - "expectedContent": "Please enter new PIN for: {secondary_session_id}\n0:Back" - }, - { - "input": "11111", - "expectedContent": "The PIN you have entered is invalid.Please try a 4 digit number instead.\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "Please enter new PIN for: {secondary_session_id}\n0:Back" - }, - { - "input": "1111", - "expectedContent": "Please confirm new PIN for: {secondary_session_id}\n0:Back" - }, - { - "input": "1111", - "expectedContent": "Please enter your PIN:" + "expectedContent": "{secondary_session_id} will get a PIN reset request.\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" }, { "input": "1234", @@ -239,7 +223,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "5", @@ -264,7 +248,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "3", @@ -284,7 +268,7 @@ }, { "input": "1234", - "expectedContent": "Balance: {balance}\n\n0:Back\n9:Quit" + "expectedContent": "{balance}\n\n0:Back\n9:Quit" }, { "input": "0", @@ -292,7 +276,7 @@ }, { "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "0", @@ -309,7 +293,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "3", @@ -337,7 +321,7 @@ }, { "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "0", @@ -354,7 +338,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -411,7 +395,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -448,7 +432,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -489,7 +473,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -526,7 +510,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -563,7 +547,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -600,7 +584,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", @@ -612,7 +596,7 @@ }, { "input": "1234", - "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\nYour alias: \n\n0:Back\n9:Quit" + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\nYour alias: Not Provided\n\n0:Back\n9:Quit" }, { "input": "0", @@ -633,7 +617,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_familyname.json b/menutraversal_test/profile_edit_start_familyname.json index 98325b0..bb8bf0d 100644 --- a/menutraversal_test/profile_edit_start_familyname.json +++ b/menutraversal_test/profile_edit_start_familyname.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_firstname.json b/menutraversal_test/profile_edit_start_firstname.json index 0f6be8b..1fa2ca6 100644 --- a/menutraversal_test/profile_edit_start_firstname.json +++ b/menutraversal_test/profile_edit_start_firstname.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_gender.json b/menutraversal_test/profile_edit_start_gender.json index afca12a..d95420f 100644 --- a/menutraversal_test/profile_edit_start_gender.json +++ b/menutraversal_test/profile_edit_start_gender.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_location.json b/menutraversal_test/profile_edit_start_location.json index 8852911..86541a3 100644 --- a/menutraversal_test/profile_edit_start_location.json +++ b/menutraversal_test/profile_edit_start_location.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_offerings.json b/menutraversal_test/profile_edit_start_offerings.json index 6aa40f6..2fc976a 100644 --- a/menutraversal_test/profile_edit_start_offerings.json +++ b/menutraversal_test/profile_edit_start_offerings.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_start_yob.json b/menutraversal_test/profile_edit_start_yob.json index 45227f7..10c1d11 100644 --- a/menutraversal_test/profile_edit_start_yob.json +++ b/menutraversal_test/profile_edit_start_yob.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/profile_edit_when_adjacent_item_set.json b/menutraversal_test/profile_edit_when_adjacent_item_set.json index f8d7263..ec7c880 100644 --- a/menutraversal_test/profile_edit_when_adjacent_item_set.json +++ b/menutraversal_test/profile_edit_when_adjacent_item_set.json @@ -9,7 +9,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "1", diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 8728640..375be80 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -116,7 +116,7 @@ }, { "input": "3", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n7:My Alias\n0:Back" }, { "input": "6", diff --git a/services/registration/address.vis b/services/registration/address.vis index dfc46d1..a078306 100644 --- a/services/registration/address.vis +++ b/services/registration/address.vis @@ -1,5 +1,4 @@ LOAD check_identifier 0 -RELOAD check_identifier MAP check_identifier MOUT back 0 MOUT quit 9 diff --git a/services/registration/amount.vis b/services/registration/amount.vis index 2266160..c50691f 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -1,12 +1,12 @@ LOAD reset_transaction_amount 0 -LOAD max_amount 10 +LOAD max_amount 40 RELOAD max_amount MAP max_amount MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount -CATCH api_failure flag_api_call_error 1 +CATCH api_failure flag_api_call_error 1 CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 LOAD get_recipient 0 diff --git a/services/registration/api_failure b/services/registration/api_failure index 06d2d9e..915b7fc 100644 --- a/services/registration/api_failure +++ b/services/registration/api_failure @@ -1 +1 @@ -Failed to connect to the custodial service.Please try again. \ No newline at end of file +Failed to connect to the custodial service .Please try again. \ No newline at end of file diff --git a/services/registration/api_failure.vis b/services/registration/api_failure.vis index 37b3deb..469b5db 100644 --- a/services/registration/api_failure.vis +++ b/services/registration/api_failure.vis @@ -1,5 +1,7 @@ +LOAD reset_api_call_failure 6 +RELOAD reset_api_call_failure MOUT retry 1 MOUT quit 9 HALT -INCMP _ 1 +INCMP ^ 1 INCMP quit 9 diff --git a/services/registration/api_failure_swa b/services/registration/api_failure_swa new file mode 100644 index 0000000..fb24f2c --- /dev/null +++ b/services/registration/api_failure_swa @@ -0,0 +1 @@ +Imeshindwa kuunganisha kwenye huduma ya uangalizi. Tafadhali jaribu tena. \ No newline at end of file diff --git a/services/registration/authorize_reset_others_pin b/services/registration/authorize_reset_others_pin new file mode 100644 index 0000000..1ed5a77 --- /dev/null +++ b/services/registration/authorize_reset_others_pin @@ -0,0 +1,2 @@ +{{.retrieve_blocked_number}} will get a PIN reset request. +Please enter your PIN to confirm: \ No newline at end of file diff --git a/services/registration/authorize_reset_others_pin.vis b/services/registration/authorize_reset_others_pin.vis new file mode 100644 index 0000000..6f9e5b3 --- /dev/null +++ b/services/registration/authorize_reset_others_pin.vis @@ -0,0 +1,12 @@ +LOAD retrieve_blocked_number 0 +RELOAD retrieve_blocked_number +MAP retrieve_blocked_number +MOUT back 0 +MOUT quit 9 +LOAD authorize_account 6 +HALT +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 +INCMP _ 0 +INCMP quit 9 +INCMP pin_reset_result * diff --git a/services/registration/authorize_reset_others_pin_swa b/services/registration/authorize_reset_others_pin_swa new file mode 100644 index 0000000..4f355ac --- /dev/null +++ b/services/registration/authorize_reset_others_pin_swa @@ -0,0 +1,2 @@ +{{.retrieve_blocked_number}} atapokea ombi la kuweka upya PIN. +Tafadhali weka PIN yako kudhibitisha: \ No newline at end of file diff --git a/services/registration/check_statement.vis b/services/registration/check_statement.vis index d79b5ca..c393a0a 100644 --- a/services/registration/check_statement.vis +++ b/services/registration/check_statement.vis @@ -1,5 +1,6 @@ LOAD check_transactions 0 RELOAD check_transactions +CATCH api_failure flag_api_call_error 1 CATCH no_transfers flag_no_transfers 1 LOAD authorize_account 6 MOUT back 0 diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index fad90cc..a748685 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,6 +1,6 @@ LOAD reset_incorrect 6 LOAD fetch_community_balance 0 -CATCH api_failure flag_api_call_error 1 +CATCH api_failure flag_api_call_error 1 MAP fetch_community_balance CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 diff --git a/services/registration/confirm_create_pin.vis b/services/registration/confirm_create_pin.vis index 02279dc..2e82d7d 100644 --- a/services/registration/confirm_create_pin.vis +++ b/services/registration/confirm_create_pin.vis @@ -1,4 +1,7 @@ -LOAD save_temporary_pin 6 +MOUT back 0 HALT +INCMP _ 0 LOAD verify_create_pin 8 +RELOAD verify_create_pin +CATCH pin_mismatch flag_pin_mismatch 1 INCMP account_creation * diff --git a/services/registration/confirm_new_alias b/services/registration/confirm_new_alias new file mode 100644 index 0000000..6cc2cc3 --- /dev/null +++ b/services/registration/confirm_new_alias @@ -0,0 +1,2 @@ +Your full alias will be: {{.get_suggested_alias}} +Please enter your PIN to confirm: diff --git a/services/registration/confirm_new_alias.vis b/services/registration/confirm_new_alias.vis new file mode 100644 index 0000000..ea79412 --- /dev/null +++ b/services/registration/confirm_new_alias.vis @@ -0,0 +1,12 @@ +LOAD reset_invalid_pin 6 +RELOAD reset_invalid_pin +LOAD get_suggested_alias 0 +RELOAD get_suggested_alias +MAP get_suggested_alias +MOUT back 0 +HALT +INCMP _ 0 +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH invalid_pin flag_invalid_pin 1 +CATCH update_alias flag_allow_update 1 diff --git a/services/registration/confirm_new_alias_swa b/services/registration/confirm_new_alias_swa new file mode 100644 index 0000000..e622e9c --- /dev/null +++ b/services/registration/confirm_new_alias_swa @@ -0,0 +1,2 @@ +Lakabu yako kamili itakuwa: {{.get_suggested_alias}} +Tafadhali weka PIN yako ili kuthibitisha: diff --git a/services/registration/confirm_others_new_pin b/services/registration/confirm_others_new_pin deleted file mode 100644 index 720778b..0000000 --- a/services/registration/confirm_others_new_pin +++ /dev/null @@ -1 +0,0 @@ -Please confirm new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis deleted file mode 100644 index 50dfb19..0000000 --- a/services/registration/confirm_others_new_pin.vis +++ /dev/null @@ -1,14 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -RELOAD retrieve_blocked_number -MAP retrieve_blocked_number -CATCH invalid_others_pin flag_valid_pin 0 -CATCH pin_reset_result flag_account_authorized 1 -LOAD save_others_temporary_pin 6 -RELOAD save_others_temporary_pin -MOUT back 0 -HALT -INCMP _ 0 -LOAD check_pin_mismatch 6 -RELOAD check_pin_mismatch -CATCH others_pin_mismatch flag_pin_mismatch 1 -INCMP pin_entry * diff --git a/services/registration/confirm_others_new_pin_swa b/services/registration/confirm_others_new_pin_swa deleted file mode 100644 index f0b09c8..0000000 --- a/services/registration/confirm_others_new_pin_swa +++ /dev/null @@ -1 +0,0 @@ -Tafadhali thibitisha PIN mpya ya: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/confirm_pin_change.vis b/services/registration/confirm_pin_change.vis index 09b12f8..419662b 100644 --- a/services/registration/confirm_pin_change.vis +++ b/services/registration/confirm_pin_change.vis @@ -1,7 +1,7 @@ -LOAD confirm_pin_change 0 MOUT back 0 HALT INCMP _ 0 +LOAD confirm_pin_change 0 RELOAD confirm_pin_change -CATCH pin_reset_mismatch flag_pin_mismatch 1 -INCMP * pin_reset_success +CATCH pin_mismatch flag_pin_mismatch 1 +INCMP pin_reset_success * diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index 40989ec..b329fc3 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -2,8 +2,8 @@ LOAD create_account 0 CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT +INCMP quit 0 LOAD save_temporary_pin 6 RELOAD save_temporary_pin -CATCH . flag_incorrect_pin 1 -INCMP quit 0 +CATCH invalid_pin flag_invalid_pin 1 INCMP confirm_create_pin * diff --git a/services/registration/create_pin_mismatch.vis b/services/registration/create_pin_mismatch.vis deleted file mode 100644 index 91793b5..0000000 --- a/services/registration/create_pin_mismatch.vis +++ /dev/null @@ -1,5 +0,0 @@ -MOUT retry 1 -MOUT quit 9 -HALT -INCMP confirm_create_pin 1 -INCMP quit 9 diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index 018aad3..56b0558 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -7,4 +7,4 @@ INCMP _ 0 LOAD validate_blocked_number 6 RELOAD validate_blocked_number CATCH unregistered_number flag_unregistered_number 1 -INCMP enter_others_new_pin * +INCMP authorize_reset_others_pin * diff --git a/services/registration/enter_others_new_pin b/services/registration/enter_others_new_pin deleted file mode 100644 index 52ae664..0000000 --- a/services/registration/enter_others_new_pin +++ /dev/null @@ -1 +0,0 @@ -Please enter new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis deleted file mode 100644 index 3f8a5c6..0000000 --- a/services/registration/enter_others_new_pin.vis +++ /dev/null @@ -1,9 +0,0 @@ -LOAD retrieve_blocked_number 0 -RELOAD retrieve_blocked_number -MAP retrieve_blocked_number -MOUT back 0 -HALT -LOAD verify_new_pin 6 -RELOAD verify_new_pin -INCMP _ 0 -INCMP * confirm_others_new_pin diff --git a/services/registration/enter_others_new_pin_swa b/services/registration/enter_others_new_pin_swa deleted file mode 100644 index 77ec2f3..0000000 --- a/services/registration/enter_others_new_pin_swa +++ /dev/null @@ -1 +0,0 @@ -Tafadhali weka PIN mpya ya: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/invalid_others_pin b/services/registration/invalid_others_pin deleted file mode 100644 index acdf45f..0000000 --- a/services/registration/invalid_others_pin +++ /dev/null @@ -1 +0,0 @@ -The PIN you have entered is invalid.Please try a 4 digit number instead. \ No newline at end of file diff --git a/services/registration/invalid_others_pin.vis b/services/registration/invalid_others_pin.vis deleted file mode 100644 index d218e6d..0000000 --- a/services/registration/invalid_others_pin.vis +++ /dev/null @@ -1,5 +0,0 @@ -MOUT retry 1 -MOUT quit 9 -HALT -INCMP enter_others_new_pin 1 -INCMP quit 9 diff --git a/services/registration/invalid_pin b/services/registration/invalid_pin index dd984ea..a5031af 100644 --- a/services/registration/invalid_pin +++ b/services/registration/invalid_pin @@ -1 +1 @@ -The PIN you entered is invalid.The PIN must be different from your current PIN.For help call +254757628885 \ No newline at end of file +The PIN you entered is invalid. The PIN must be a 4 digit number. \ No newline at end of file diff --git a/services/registration/invalid_pin.vis b/services/registration/invalid_pin.vis index 3790a08..34e4193 100644 --- a/services/registration/invalid_pin.vis +++ b/services/registration/invalid_pin.vis @@ -1,3 +1,8 @@ -MOUT back 0 +LOAD reset_invalid_pin 6 +RELOAD reset_invalid_pin +MOUT retry 1 +MOUT quit 9 HALT -INCMP _ 0 +INCMP _ 1 +INCMP quit 9 +INCMP . * diff --git a/services/registration/invalid_pin_swa b/services/registration/invalid_pin_swa index 7512242..bba32c7 100644 --- a/services/registration/invalid_pin_swa +++ b/services/registration/invalid_pin_swa @@ -1 +1 @@ -PIN mpya na udhibitisho wa PIN mpya hazilingani.Tafadhali jaribu tena.Kwa usaidizi piga simu +254757628885. +PIN uliyoweka si sahihi. PIN lazima iwe nambari 4. \ No newline at end of file diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index 8f10e7d..0c2156f 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -33,3 +33,6 @@ msgstr "Sarafu: %s\nSalio: %s" msgid "Your request has been sent. You will receive an SMS when your %s %s has been swapped for %s." msgstr "Ombi lako limetumwa. Utapokea SMS wakati %s %s yako itakapobadilishwa kuwa %s." + +msgid "%s balance: %s\n" +msgstr "%s salio: %s\n" \ No newline at end of file diff --git a/services/registration/main.vis b/services/registration/main.vis index 69f8b00..ca79212 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,9 +1,8 @@ LOAD clear_temporary_value 2 RELOAD clear_temporary_value -LOAD set_default_voucher 8 -RELOAD set_default_voucher -LOAD check_vouchers 10 -RELOAD check_vouchers +LOAD manage_vouchers 160 +RELOAD manage_vouchers +CATCH api_failure flag_api_call_error 1 LOAD check_balance 128 RELOAD check_balance CATCH api_failure flag_api_call_error 1 diff --git a/services/registration/my_account.vis b/services/registration/my_account.vis index e3956d2..fc0a634 100644 --- a/services/registration/my_account.vis +++ b/services/registration/my_account.vis @@ -1,3 +1,4 @@ +LOAD authorize_account 16 LOAD reset_allow_update 0 MOUT profile 1 MOUT change_language 2 @@ -5,13 +6,15 @@ MOUT check_balance 3 MOUT check_statement 4 MOUT pin_options 5 MOUT my_address 6 +MOUT my_account_alias 7 MOUT back 0 HALT -INCMP main 0 +INCMP ^ 0 INCMP edit_profile 1 INCMP change_language 2 INCMP balances 3 INCMP check_statement 4 INCMP pin_management 5 INCMP address 6 +INCMP my_account_alias 7 INCMP . * diff --git a/services/registration/my_account_alias b/services/registration/my_account_alias new file mode 100644 index 0000000..4ab2e04 --- /dev/null +++ b/services/registration/my_account_alias @@ -0,0 +1,2 @@ +Current alias: {{.get_current_profile_info}} +Enter your preferred alias: \ No newline at end of file diff --git a/services/registration/my_account_alias.vis b/services/registration/my_account_alias.vis new file mode 100644 index 0000000..c44cce1 --- /dev/null +++ b/services/registration/my_account_alias.vis @@ -0,0 +1,9 @@ +LOAD get_current_profile_info 0 +MAP get_current_profile_info +MOUT back 0 +HALT +INCMP _ 0 +LOAD request_custom_alias 0 +RELOAD request_custom_alias +CATCH api_failure flag_api_call_error 1 +INCMP confirm_new_alias * diff --git a/services/registration/my_account_alias_menu b/services/registration/my_account_alias_menu new file mode 100644 index 0000000..3351fdf --- /dev/null +++ b/services/registration/my_account_alias_menu @@ -0,0 +1 @@ +My Alias \ No newline at end of file diff --git a/services/registration/my_account_alias_menu_swa b/services/registration/my_account_alias_menu_swa new file mode 100644 index 0000000..fdb3f58 --- /dev/null +++ b/services/registration/my_account_alias_menu_swa @@ -0,0 +1 @@ +Lakabu yangu \ No newline at end of file diff --git a/services/registration/my_account_alias_swa b/services/registration/my_account_alias_swa new file mode 100644 index 0000000..3bd52a3 --- /dev/null +++ b/services/registration/my_account_alias_swa @@ -0,0 +1,2 @@ +Lakabu ya sasa: {{.get_current_profile_info}} +Weka lakabu unalopendelea: \ No newline at end of file diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index b6094c0..166d18f 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,6 +1,6 @@ LOAD reset_incorrect 6 LOAD check_balance 0 -CATCH api_failure flag_api_call_error 1 +CATCH api_failure flag_api_call_error 1 MAP check_balance CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 diff --git a/services/registration/new_pin.vis b/services/registration/new_pin.vis index 56705d7..79e95ea 100644 --- a/services/registration/new_pin.vis +++ b/services/registration/new_pin.vis @@ -2,6 +2,5 @@ MOUT back 0 HALT INCMP _ 0 RELOAD save_temporary_pin -RELOAD verify_new_pin -CATCH invalid_pin flag_valid_pin 0 -INCMP * confirm_pin_change +CATCH invalid_pin flag_invalid_pin 1 +INCMP confirm_pin_change * diff --git a/services/registration/old_pin.vis b/services/registration/old_pin.vis index aa6eabf..ccc2928 100644 --- a/services/registration/old_pin.vis +++ b/services/registration/old_pin.vis @@ -1,8 +1,8 @@ -RELOAD reset_allow_update +RELOAD reset_incorrect MOUT back 0 HALT INCMP _ 0 RELOAD authorize_account CATCH incorrect_pin flag_incorrect_pin 1 -CATCH _ flag_allow_update 0 +CATCH invalid_pin flag_invalid_pin 1 INCMP new_pin * diff --git a/services/registration/others_pin_mismatch b/services/registration/others_pin_mismatch deleted file mode 100644 index deb9fe5..0000000 --- a/services/registration/others_pin_mismatch +++ /dev/null @@ -1 +0,0 @@ -The PIN you have entered is not a match diff --git a/services/registration/others_pin_mismatch_swa b/services/registration/others_pin_mismatch_swa deleted file mode 100644 index 5787790..0000000 --- a/services/registration/others_pin_mismatch_swa +++ /dev/null @@ -1 +0,0 @@ -PIN uliyoweka hailingani.Jaribu tena. \ No newline at end of file diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index 409b3c8..01bd236 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -1,9 +1,9 @@ LOAD set_back 6 -LOAD authorize_account 5 +LOAD authorize_account 16 LOAD reset_allow_update 4 -LOAD verify_new_pin 2 LOAD save_temporary_pin 1 LOAD reset_incorrect 0 +LOAD reset_invalid_pin 6 MOUT change_pin 1 MOUT reset_pin 2 MOUT back 0 diff --git a/services/registration/create_pin_mismatch b/services/registration/pin_mismatch similarity index 100% rename from services/registration/create_pin_mismatch rename to services/registration/pin_mismatch diff --git a/services/registration/others_pin_mismatch.vis b/services/registration/pin_mismatch.vis similarity index 100% rename from services/registration/others_pin_mismatch.vis rename to services/registration/pin_mismatch.vis diff --git a/services/registration/create_pin_mismatch_swa b/services/registration/pin_mismatch_swa similarity index 100% rename from services/registration/create_pin_mismatch_swa rename to services/registration/pin_mismatch_swa diff --git a/services/registration/pin_reset_mismatch b/services/registration/pin_reset_mismatch deleted file mode 100644 index dc0236b..0000000 --- a/services/registration/pin_reset_mismatch +++ /dev/null @@ -1 +0,0 @@ -The PIN is not a match. Try again diff --git a/services/registration/pin_reset_mismatch.vis b/services/registration/pin_reset_mismatch.vis deleted file mode 100644 index b2421aa..0000000 --- a/services/registration/pin_reset_mismatch.vis +++ /dev/null @@ -1,6 +0,0 @@ -MOUT retry 1 -MOUT quit 9 -HALT -INCMP _ 1 -INCMP quit 9 -INCMP . * diff --git a/services/registration/pin_reset_mismatch_swa b/services/registration/pin_reset_mismatch_swa deleted file mode 100644 index 5787790..0000000 --- a/services/registration/pin_reset_mismatch_swa +++ /dev/null @@ -1 +0,0 @@ -PIN uliyoweka hailingani.Jaribu tena. \ No newline at end of file diff --git a/services/registration/pin_reset_result.vis b/services/registration/pin_reset_result.vis index de877e5..3412473 100644 --- a/services/registration/pin_reset_result.vis +++ b/services/registration/pin_reset_result.vis @@ -1,3 +1,4 @@ +CATCH _ flag_account_authorized 0 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number LOAD reset_others_pin 6 @@ -5,4 +6,4 @@ MOUT back 0 MOUT quit 9 HALT INCMP ^ 0 -INCMP quit 9 +INCMP quit 9 diff --git a/services/registration/pin_reset_success.vis b/services/registration/pin_reset_success.vis index a3a143f..5535616 100644 --- a/services/registration/pin_reset_success.vis +++ b/services/registration/pin_reset_success.vis @@ -1,6 +1,6 @@ MOUT back 0 MOUT quit 9 HALT -INCMP main 0 +INCMP ^ 0 INCMP quit 9 INCMP . * diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 17f8460..fa67938 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -9,7 +9,7 @@ flag,flag_account_authorized,15,this is set to allow a user access guarded nodes flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid -flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,flag_incorrect_pin,19,this is set when the provided PIN does not match the current account's PIN flag,flag_valid_pin,20,this is set when the given PIN is valid flag,flag_allow_update,21,this is set to allow a user to update their profile data flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth @@ -29,5 +29,9 @@ flag,flag_location_set,35,this is set when the location of the profile is set flag,flag_offerings_set,36,this is set when the offerings of the profile is set flag,flag_back_set,37,this is set when it is a back navigation flag,flag_account_blocked,38,this is set when an account has been blocked after the allowed incorrect PIN attempts have been exceeded -flag,flag_incorrect_pool,39,this is set when the user selects an invalid pool -flag,flag_low_swap_amount,40,this is set when the swap max limit is less than 0.1 +flag,flag_invalid_pin,39,this is set when the given PIN is invalid(is less than or more than 4 digits) +flag,flag_alias_set,40,this is set when an account alias has been assigned to a user +flag,flag_account_pin_reset,41,this is set on an account when an admin triggers a PIN reset for themflag,flag_incorrect_pool,39,this is set when the user selects an invalid pool +flag,flag_incorrect_pool,42,this is set when the user selects an invalid pool +flag,flag_low_swap_amount,43,this is set when the swap max limit is less than 0.1 + diff --git a/services/registration/root.vis b/services/registration/root.vis index 3c78bea..035db83 100644 --- a/services/registration/root.vis +++ b/services/registration/root.vis @@ -1,12 +1,15 @@ LOAD check_blocked_status 1 RELOAD check_blocked_status +LOAD check_account_created 2 +RELOAD check_account_created +CATCH self_reset_pin flag_account_pin_reset 1 CATCH blocked_account flag_account_blocked 1 CATCH select_language flag_language_set 0 CATCH terms flag_account_created 0 +CATCH create_pin flag_pin_set 0 LOAD check_account_status 0 RELOAD check_account_status -CATCH api_failure flag_api_call_error 1 +CATCH api_failure flag_api_call_error 1 CATCH account_pending flag_account_pending 1 -CATCH create_pin flag_pin_set 0 CATCH main flag_account_success 1 HALT diff --git a/services/registration/self_reset_pin b/services/registration/self_reset_pin new file mode 100644 index 0000000..ce40705 --- /dev/null +++ b/services/registration/self_reset_pin @@ -0,0 +1,2 @@ +A PIN reset has been done on your account. +Please enter a new four number PIN: \ No newline at end of file diff --git a/services/registration/self_reset_pin.vis b/services/registration/self_reset_pin.vis new file mode 100644 index 0000000..401a4e0 --- /dev/null +++ b/services/registration/self_reset_pin.vis @@ -0,0 +1,6 @@ +LOAD reset_invalid_pin 6 +HALT +LOAD save_temporary_pin 1 +RELOAD save_temporary_pin +CATCH invalid_pin flag_invalid_pin 1 +INCMP confirm_pin_change * diff --git a/services/registration/self_reset_pin_swa b/services/registration/self_reset_pin_swa new file mode 100644 index 0000000..0ef19d0 --- /dev/null +++ b/services/registration/self_reset_pin_swa @@ -0,0 +1,2 @@ +Uwekaji upya wa PIN umefanyika kwenye akaunti yako. +Tafadhali weka PIN mpya ya nambari nne: \ No newline at end of file diff --git a/services/registration/send.vis b/services/registration/send.vis index 369bb67..dfdcd70 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -5,6 +5,7 @@ MOUT back 0 HALT LOAD validate_recipient 50 RELOAD validate_recipient +CATCH api_failure flag_api_call_error 1 CATCH invalid_recipient flag_invalid_recipient 1 CATCH invite_recipient flag_invalid_recipient_with_invite 1 INCMP _ 0 diff --git a/services/registration/update_alias b/services/registration/update_alias new file mode 100644 index 0000000..30f4a65 --- /dev/null +++ b/services/registration/update_alias @@ -0,0 +1 @@ +Your alias has been updated successfully \ No newline at end of file diff --git a/services/registration/update_alias.vis b/services/registration/update_alias.vis new file mode 100644 index 0000000..fcbfc17 --- /dev/null +++ b/services/registration/update_alias.vis @@ -0,0 +1,7 @@ +LOAD confirm_new_alias 0 +RELOAD confirm_new_alias +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 diff --git a/services/registration/update_alias_swa b/services/registration/update_alias_swa new file mode 100644 index 0000000..bc60faf --- /dev/null +++ b/services/registration/update_alias_swa @@ -0,0 +1 @@ +Ombi lako la kubadilisha lakabu limefanikiwa. \ No newline at end of file diff --git a/services/registration/voucher_details.vis b/services/registration/voucher_details.vis index 1b009f1..628e034 100644 --- a/services/registration/voucher_details.vis +++ b/services/registration/voucher_details.vis @@ -1,5 +1,7 @@ CATCH no_voucher flag_no_active_voucher 1 LOAD get_voucher_details 0 +RELOAD get_voucher_details +CATCH api_failure flag_api_call_error 1 MAP get_voucher_details MOUT back 0 HALT diff --git a/services/remote.go b/services/remote.go index baad08f..07e78fe 100644 --- a/services/remote.go +++ b/services/remote.go @@ -14,6 +14,6 @@ import ( func New(ctx context.Context, storageService storage.StorageService) remote.AccountService { return &httpremote.HTTPAccountService{ SS: storageService, - UseApi: false, + UseApi: true, } } diff --git a/store/db/db.go b/store/db/db.go index 50a09cd..e6e1eef 100644 --- a/store/db/db.go +++ b/store/db/db.go @@ -63,6 +63,10 @@ const ( DATA_INITIAL_LANGUAGE_CODE //Fully qualified account alias string DATA_ACCOUNT_ALIAS + //currently suggested alias by the api awaiting user's confirmation as accepted account alias + DATA_SUGGESTED_ALIAS + //Key used to store a value of 1 for a user to reset their own PIN once they access the menu. + DATA_SELF_PIN_RESET // Holds the active pool contract address for the swap DATA_ACTIVE_POOL_ADDRESS // Currently active swap from symbol for the swap @@ -173,7 +177,8 @@ func StringToDataTyp(str string) (DataTyp, error) { return DATA_GENDER, nil case "DATA_OFFERINGS": return DATA_OFFERINGS, nil - + case "DATA_ACCOUNT_ALIAS": + return DATA_ACCOUNT_ALIAS, nil default: return 0, errors.New("invalid DataTyp string") } diff --git a/store/db/sub_prefix_db.go b/store/db/sub_prefix_db.go index ae59171..156fbc2 100644 --- a/store/db/sub_prefix_db.go +++ b/store/db/sub_prefix_db.go @@ -33,11 +33,14 @@ func (s *SubPrefixDb) toKey(k []byte) []byte { func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { s.store.SetPrefix(db.DATATYPE_USERDATA) key = s.toKey(key) + logg.InfoCtxf(ctx, "SubPrefixDb Get log", "key", string(key)) + return s.store.Get(ctx, key) } func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { s.store.SetPrefix(db.DATATYPE_USERDATA) key = s.toKey(key) + logg.InfoCtxf(ctx, "SubPrefixDb Put log", "key", string(key)) return s.store.Put(ctx, key, val) } diff --git a/store/log_db.go b/store/log_db.go new file mode 100644 index 0000000..d056a2e --- /dev/null +++ b/store/log_db.go @@ -0,0 +1,27 @@ +package store + +import ( + "context" + + visedb "git.defalsify.org/vise.git/db" + "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" + storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" +) + +type LogDb struct { + visedb.Db +} + +func (db *LogDb) WriteLogEntry(ctx context.Context, sessionId string, typ db.DataTyp, v []byte) error { + db.SetPrefix(visedb.DATATYPE_USERDATA) + db.SetSession(sessionId) + k := storedb.ToBytes(typ) + return db.Put(ctx, k, v) +} + +func (db *LogDb) ReadLogEntry(ctx context.Context, sessionId string, typ db.DataTyp) ([]byte, error) { + db.SetPrefix(visedb.DATATYPE_USERDATA) + db.SetSession(sessionId) + k := storedb.ToBytes(typ) + return db.Get(ctx, k) +} diff --git a/store/vouchers.go b/store/vouchers.go index 0569a60..94ebc5a 100644 --- a/store/vouchers.go +++ b/store/vouchers.go @@ -68,7 +68,7 @@ func ScaleDownBalance(balance, decimals string) string { } // GetVoucherData retrieves and matches voucher data -func GetVoucherData(ctx context.Context, db storedb.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { +func GetVoucherData(ctx context.Context, store DataStore, sessionId string, input string) (*dataserviceapi.TokenHoldings, error) { keys := []storedb.DataTyp{ storedb.DATA_VOUCHER_SYMBOLS, storedb.DATA_VOUCHER_BALANCES, @@ -78,9 +78,9 @@ func GetVoucherData(ctx context.Context, db storedb.PrefixDb, input string) (*da data := make(map[storedb.DataTyp]string) for _, key := range keys { - value, err := db.Get(ctx, storedb.ToBytes(key)) + value, err := store.ReadEntry(ctx, sessionId, key) if err != nil { - return nil, fmt.Errorf("failed to get prefix key %x: %v", storedb.ToBytes(key), err) + return nil, fmt.Errorf("failed to get data key %x: %v", key, err) } data[key] = string(value) } diff --git a/store/vouchers_test.go b/store/vouchers_test.go index a98ffc3..711f93b 100644 --- a/store/vouchers_test.go +++ b/store/vouchers_test.go @@ -8,7 +8,6 @@ import ( "github.com/alecthomas/assert/v2" "github.com/stretchr/testify/require" - visedb "git.defalsify.org/vise.git/db" memdb "git.defalsify.org/vise.git/db/mem" storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" @@ -77,16 +76,8 @@ func TestProcessVouchers(t *testing.T) { } func TestGetVoucherData(t *testing.T) { - ctx := context.Background() - - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - - prefix := storedb.ToBytes(visedb.DATATYPE_USERDATA) - spdb := storedb.NewSubPrefixDb(db, prefix) + ctx, store := InitializeTestDb(t) + sessionId := "session123" // Test voucher data mockData := map[storedb.DataTyp][]byte{ @@ -98,13 +89,13 @@ func TestGetVoucherData(t *testing.T) { // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(storedb.ToBytes(key)), []byte(value)) + err := store.WriteEntry(ctx, sessionId, key, []byte(value)) if err != nil { t.Fatal(err) } } - result, err := GetVoucherData(ctx, spdb, "1") + result, err := GetVoucherData(ctx, store, sessionId, "1") assert.NoError(t, err) assert.Equal(t, "SRF", result.TokenSymbol) diff --git a/testutil/engine.go b/testutil/engine.go index 0cae8d4..531481d 100644 --- a/testutil/engine.go +++ b/testutil/engine.go @@ -113,6 +113,12 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe os.Exit(1) } + logdb, err := menuStorageService.GetLogDb(ctx, userDataStore, "test-db-logs", "user-data") + if err != nil { + fmt.Fprintf(os.Stderr, "get log db error: %v\n", err) + os.Exit(1) + } + dbResource, ok := rs.(*resource.DbResource) if !ok { fmt.Fprintf(os.Stderr, "dbresource cast error") @@ -121,6 +127,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userDataStore) + lhs.SetLogDb(&logdb) lhs.SetPersister(pe) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) @@ -154,6 +161,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool, *persist.Pe en := lhs.GetEngine(lhs.Cfg, rs, pe) cleanFn := func() { + logdb.Close(ctx) err := en.Finish(ctx) if err != nil { logg.Errorf(err.Error())