Compare commits
98 Commits
tests-refa
...
pre-mock-r
| Author | SHA1 | Date | |
|---|---|---|---|
| 2587882eae | |||
|
|
6dbe74d12b
|
||
|
|
332074375a
|
||
|
|
5e4a9e7567
|
||
|
|
eb2c73dce1
|
||
|
|
7e448f739a
|
||
|
|
0014693ba8
|
||
| 45945ae9c5 | |||
|
|
d7ea8fa651
|
||
|
|
e75aa2c1f7 | ||
|
|
63eed81d3d
|
||
|
|
f4ca4454ea
|
||
|
|
2704069e74
|
||
|
|
7d1a04f089
|
||
|
|
53fa6f64ce
|
||
|
|
7fa38340dd
|
||
|
|
7aab3cff8c
|
||
|
|
299534ccf1
|
||
|
|
b2655b7f11
|
||
|
|
5abe9b78cc
|
||
|
|
12825ae08a
|
||
|
|
ac0b4b2ed1
|
||
| 5f666382ab | |||
|
|
ce917d9e89
|
||
|
|
3ce25d0e14
|
||
|
|
8fe8ff540b
|
||
|
|
0b4bf58107
|
||
| 074345fcf9 | |||
|
|
e6a369dcdd
|
||
|
|
b8bbd88078
|
||
|
|
d25128287e
|
||
|
|
c45fcda2f1
|
||
|
|
211cc1f775
|
||
|
|
981f7ca4f6
|
||
|
|
05ed236e03
|
||
|
|
767a3cd64c
|
||
|
|
c4078c5280
|
||
|
|
d434194021
|
||
|
|
c6ca3f6be4
|
||
|
|
8093eae61a
|
||
|
|
833d52a558
|
||
|
|
8262e14198
|
||
|
|
a31cac4e50
|
||
|
|
8b399781e8
|
||
|
|
caafe495be
|
||
|
|
66b34eaea4
|
||
|
|
ea4c6d9314
|
||
|
|
7c823e07ca
|
||
|
|
41585f831c
|
||
|
|
d93a26f9b0
|
||
|
|
cf523e30f8
|
||
|
|
888d3befe9
|
||
|
|
017691a40c
|
||
|
|
dc418771a7
|
||
|
|
c2068db050
|
||
|
|
c42b1cd66b
|
||
|
|
b404ae95fb
|
||
|
|
adaa0c63ef
|
||
|
|
843b0d1e7e | ||
|
|
cb997159f7
|
||
|
|
7241cdbfcb
|
||
|
|
0480c02633
|
||
|
|
0a97f610a4
|
||
|
|
5a0563df94
|
||
|
|
7597b96dae
|
||
|
|
f37483e2f0
|
||
|
|
d0ad6395b5
|
||
|
|
106983a394
|
||
|
|
91b85af11a
|
||
|
|
534d756318
|
||
|
|
6998c30dd1
|
||
|
|
449f90c95b
|
||
|
|
e96c874300
|
||
|
|
b35460d3c1
|
||
|
|
124049c924
|
||
|
|
5fd3eb3c29
|
||
|
|
d83962c0ba
|
||
|
|
41da099933
|
||
|
|
c9bb93ede6
|
||
|
|
ca13d9155c
|
||
|
|
e338ce0025
|
||
|
|
03c32fd265 | ||
| 727f54ee57 | |||
|
|
b97965193b
|
||
|
|
aec0abb2b6
|
||
|
|
2c361e5b96
|
||
|
|
131f3bcf46
|
||
|
|
520f5abdcd
|
||
|
|
5d294b663c
|
||
|
|
26073c8000
|
||
|
|
e4c2f644f3
|
||
|
|
3de46cef5e
|
||
|
|
0cc0bdf9f7
|
||
|
|
72d5c186dd
|
||
|
|
dc782d87a8
|
||
|
|
ddae746b9d
|
||
|
|
6c3ff0e9db | ||
|
|
3ef64271e7
|
@@ -16,3 +16,7 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create
|
|||||||
TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/
|
TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/
|
||||||
BALANCE_URL=https://custodial.sarafu.africa/api/account/status/
|
BALANCE_URL=https://custodial.sarafu.africa/api/account/status/
|
||||||
TRACK_URL=http://localhost:5003/api/v2/account/status
|
TRACK_URL=http://localhost:5003/api/v2/account/status
|
||||||
|
|
||||||
|
|
||||||
|
#numbers with privileges to reset others pin
|
||||||
|
ADMIN_NUMBERS=254051722XXX,255012221XXX
|
||||||
@@ -131,7 +131,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
lhs.SetDataStore(&userdataStore)
|
lhs.SetDataStore(&userdataStore)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -104,8 +104,9 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
lhs.SetDataStore(&userdataStore)
|
lhs.SetDataStore(&userdataStore)
|
||||||
|
|
||||||
accountService := server.AccountService{}
|
accountService := server.AccountService{}
|
||||||
|
|
||||||
hl, err := lhs.GetHandler(&accountService)
|
hl, err := lhs.GetHandler(&accountService)
|
||||||
|
|||||||
@@ -92,13 +92,14 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
lhs.SetDataStore(&userdataStore)
|
lhs.SetDataStore(&userdataStore)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountService := server.AccountService{}
|
accountService := server.AccountService{}
|
||||||
hl, err := lhs.GetHandler(&accountService)
|
hl, err := lhs.GetHandler(&accountService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
lhs.SetDataStore(&userdatastore)
|
lhs.SetDataStore(&userdatastore)
|
||||||
lhs.SetPersister(pe)
|
lhs.SetPersister(pe)
|
||||||
|
|
||||||
|
|||||||
18
common/hex.go
Normal file
18
common/hex.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NormalizeHex(s string) (string, error) {
|
||||||
|
if len(s) >= 2 {
|
||||||
|
if s[:2] == "0x" {
|
||||||
|
s = s[2:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(r), nil
|
||||||
|
}
|
||||||
7
devtools/admin/admin_numbers.json
Normal file
7
devtools/admin/admin_numbers.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"admins": [
|
||||||
|
{
|
||||||
|
"phonenumber" : "<replace with any admin number to test with >"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
47
devtools/admin/commands/seed.go
Normal file
47
devtools/admin/commands/seed.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("adminstore")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Admin struct {
|
||||||
|
PhoneNumber string `json:"phonenumber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Admins []Admin `json:"admins"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Seed(ctx context.Context) error {
|
||||||
|
var config Config
|
||||||
|
adminstore, err := utils.NewAdminStore(ctx, "../admin_numbers")
|
||||||
|
store := adminstore.FsStore
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer store.Close()
|
||||||
|
data, err := os.ReadFile("admin_numbers.json")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, admin := range config.Admins {
|
||||||
|
err := store.Put(ctx, []byte(admin.PhoneNumber), []byte("1"))
|
||||||
|
if err != nil {
|
||||||
|
logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
devtools/admin/main.go
Normal file
17
devtools/admin/main.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/devtools/admin/commands"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
err := commands.Seed(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to initialize a list of admins with error %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -12,6 +12,8 @@ require (
|
|||||||
gopkg.in/leonelquinteros/gotext.v1 v1.3.1
|
gopkg.in/leonelquinteros/gotext.v1 v1.3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -18,6 +18,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1
|
|||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
|
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/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
|
||||||
|
github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU=
|
||||||
|
github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw=
|
||||||
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
|
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
|
||||||
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
|
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 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/asm"
|
"git.defalsify.org/vise.git/asm"
|
||||||
"git.defalsify.org/vise.git/db"
|
"git.defalsify.org/vise.git/db"
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
@@ -8,6 +10,7 @@ import (
|
|||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/server"
|
"git.grassecon.net/urdt/ussd/internal/handlers/server"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HandlerService interface {
|
type HandlerService interface {
|
||||||
@@ -28,20 +31,26 @@ type LocalHandlerService struct {
|
|||||||
DbRs *resource.DbResource
|
DbRs *resource.DbResource
|
||||||
Pe *persist.Persister
|
Pe *persist.Persister
|
||||||
UserdataStore *db.Db
|
UserdataStore *db.Db
|
||||||
|
AdminStore *utils.AdminStore
|
||||||
Cfg engine.Config
|
Cfg engine.Config
|
||||||
Rs resource.Resource
|
Rs resource.Resource
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalHandlerService(fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) {
|
func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) {
|
||||||
parser, err := getParser(fp, debug)
|
parser, err := getParser(fp, debug)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
adminstore, err := utils.NewAdminStore(ctx, "admin_numbers")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return &LocalHandlerService{
|
return &LocalHandlerService{
|
||||||
Parser: parser,
|
Parser: parser,
|
||||||
DbRs: dbResource,
|
DbRs: dbResource,
|
||||||
Cfg: cfg,
|
AdminStore: adminstore,
|
||||||
Rs: rs,
|
Cfg: cfg,
|
||||||
|
Rs: rs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +63,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) {
|
func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) {
|
||||||
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService)
|
ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -98,6 +107,13 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn
|
|||||||
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
|
ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList)
|
||||||
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher)
|
ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher)
|
||||||
ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher)
|
ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher)
|
||||||
|
ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin)
|
||||||
|
ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch)
|
||||||
|
ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber)
|
||||||
|
ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber)
|
||||||
|
ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber)
|
||||||
|
ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin)
|
||||||
|
ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin)
|
||||||
|
|
||||||
return ussdHandlers, nil
|
return ussdHandlers, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"git.defalsify.org/vise.git/persist"
|
"git.defalsify.org/vise.git/persist"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
"git.defalsify.org/vise.git/state"
|
"git.defalsify.org/vise.git/state"
|
||||||
|
"git.grassecon.net/urdt/ussd/common"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/server"
|
"git.grassecon.net/urdt/ussd/internal/handlers/server"
|
||||||
"git.grassecon.net/urdt/ussd/internal/utils"
|
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||||
"gopkg.in/leonelquinteros/gotext.v1"
|
"gopkg.in/leonelquinteros/gotext.v1"
|
||||||
@@ -32,7 +33,12 @@ var (
|
|||||||
translationDir = path.Join(scriptDir, "locale")
|
translationDir = path.Join(scriptDir, "locale")
|
||||||
okResponse *api.OKResponse
|
okResponse *api.OKResponse
|
||||||
errResponse *api.ErrResponse
|
errResponse *api.ErrResponse
|
||||||
backOption = []byte("0")
|
)
|
||||||
|
|
||||||
|
// Define the regex patterns as constants
|
||||||
|
const (
|
||||||
|
phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}`
|
||||||
|
pinPattern = `^\d{4}$`
|
||||||
)
|
)
|
||||||
|
|
||||||
// FlagManager handles centralized flag management
|
// FlagManager handles centralized flag management
|
||||||
@@ -63,34 +69,43 @@ type Handlers struct {
|
|||||||
st *state.State
|
st *state.State
|
||||||
ca cache.Memory
|
ca cache.Memory
|
||||||
userdataStore utils.DataStore
|
userdataStore utils.DataStore
|
||||||
|
adminstore *utils.AdminStore
|
||||||
flagManager *asm.FlagParser
|
flagManager *asm.FlagParser
|
||||||
accountService server.AccountServiceInterface
|
accountService server.AccountServiceInterface
|
||||||
|
prefixDb storage.PrefixDb
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) {
|
func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService server.AccountServiceInterface) (*Handlers, error) {
|
||||||
if userdataStore == nil {
|
if userdataStore == nil {
|
||||||
return nil, fmt.Errorf("cannot create handler with nil userdata store")
|
return nil, fmt.Errorf("cannot create handler with nil userdata store")
|
||||||
}
|
}
|
||||||
userDb := &utils.UserDataStore{
|
userDb := &utils.UserDataStore{
|
||||||
Db: userdataStore,
|
Db: userdataStore,
|
||||||
}
|
}
|
||||||
|
// Instantiate the SubPrefixDb with "vouchers" prefix
|
||||||
|
prefixDb := storage.NewSubPrefixDb(userdataStore, []byte("vouchers"))
|
||||||
|
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: userDb,
|
userdataStore: userDb,
|
||||||
flagManager: appFlags,
|
flagManager: appFlags,
|
||||||
|
adminstore: adminstore,
|
||||||
accountService: accountService,
|
accountService: accountService,
|
||||||
|
prefixDb: prefixDb,
|
||||||
}
|
}
|
||||||
return h, nil
|
return h, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the regex pattern as a constant
|
|
||||||
const pinPattern = `^\d{4}$`
|
|
||||||
|
|
||||||
// isValidPIN checks whether the given input is a 4 digit number
|
// isValidPIN checks whether the given input is a 4 digit number
|
||||||
func isValidPIN(pin string) bool {
|
func isValidPIN(pin string) bool {
|
||||||
match, _ := regexp.MatchString(pinPattern, pin)
|
match, _ := regexp.MatchString(pinPattern, pin)
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidPhoneNumber(phonenumber string) bool {
|
||||||
|
match, _ := regexp.MatchString(phoneRegex, phonenumber)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
|
func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
|
||||||
if h.pe != nil {
|
if h.pe != nil {
|
||||||
panic("persister already set")
|
panic("persister already set")
|
||||||
@@ -101,13 +116,25 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers {
|
|||||||
|
|
||||||
func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var r resource.Result
|
var r resource.Result
|
||||||
|
|
||||||
if h.pe == nil {
|
if h.pe == nil {
|
||||||
logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca)
|
logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca)
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
h.st = h.pe.GetState()
|
h.st = h.pe.GetState()
|
||||||
h.ca = h.pe.GetMemory()
|
h.ca = h.pe.GetMemory()
|
||||||
|
|
||||||
|
sessionId, _ := ctx.Value("SessionId").(string)
|
||||||
|
flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege")
|
||||||
|
|
||||||
|
isAdmin, _ := h.adminstore.IsAdmin(sessionId)
|
||||||
|
|
||||||
|
if isAdmin {
|
||||||
|
r.FlagSet = append(r.FlagSet, flag_admin_privilege)
|
||||||
|
} else {
|
||||||
|
r.FlagReset = append(r.FlagReset, flag_admin_privilege)
|
||||||
|
}
|
||||||
|
|
||||||
if h.st == nil || h.ca == nil {
|
if h.st == nil || h.ca == nil {
|
||||||
logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca)
|
logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca)
|
||||||
return r, fmt.Errorf("cannot get state and memory for handler")
|
return r, fmt.Errorf("cannot get state and memory for handler")
|
||||||
@@ -154,13 +181,21 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r
|
|||||||
utils.DATA_TRACKING_ID: trackingId,
|
utils.DATA_TRACKING_ID: trackingId,
|
||||||
utils.DATA_PUBLIC_KEY: publicKey,
|
utils.DATA_PUBLIC_KEY: publicKey,
|
||||||
}
|
}
|
||||||
|
store := h.userdataStore
|
||||||
for key, value := range data {
|
for key, value := range data {
|
||||||
store := h.userdataStore
|
err = store.WriteEntry(ctx, sessionId, key, []byte(value))
|
||||||
err := store.WriteEntry(ctx, sessionId, key, []byte(value))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
publicKeyNormalized, err := common.NormalizeHex(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
res.FlagSet = append(res.FlagSet, flag_account_created)
|
res.FlagSet = append(res.FlagSet, flag_account_created)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
@@ -190,6 +225,30 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte)
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) CheckPinMisMatch(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")
|
||||||
|
}
|
||||||
|
store := h.userdataStore
|
||||||
|
blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(temporaryPin, input) {
|
||||||
|
res.FlagReset = append(res.FlagReset, flag_pin_mismatch)
|
||||||
|
} else {
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_pin_mismatch)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
res := resource.Result{}
|
res := resource.Result{}
|
||||||
_, ok := ctx.Value("SessionId").(string)
|
_, ok := ctx.Value("SessionId").(string)
|
||||||
@@ -208,7 +267,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_PIN
|
// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE
|
||||||
// during the account creation process
|
// during the account creation process
|
||||||
// and during the change PIN process
|
// and during the change PIN process
|
||||||
func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
@@ -221,7 +280,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
|
|||||||
}
|
}
|
||||||
|
|
||||||
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
|
flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin")
|
||||||
|
|
||||||
accountPIN := string(input)
|
accountPIN := string(input)
|
||||||
|
|
||||||
// Validate that the PIN is a 4-digit number
|
// Validate that the PIN is a 4-digit number
|
||||||
@@ -229,11 +287,32 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt
|
|||||||
res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
|
res.FlagSet = append(res.FlagSet, flag_incorrect_pin)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
|
res.FlagReset = append(res.FlagReset, flag_incorrect_pin)
|
||||||
|
store := h.userdataStore
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
|
var res resource.Result
|
||||||
|
var err error
|
||||||
|
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN))
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
|
if !ok {
|
||||||
|
return res, fmt.Errorf("missing session")
|
||||||
|
}
|
||||||
|
temporaryPin := string(input)
|
||||||
|
blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@@ -250,7 +329,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt
|
|||||||
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
|
flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch")
|
||||||
|
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN)
|
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@@ -281,11 +360,10 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte
|
|||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN)
|
temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.Equal(input, temporaryPin) {
|
if bytes.Equal(input, temporaryPin) {
|
||||||
res.FlagSet = []uint32{flag_valid_pin}
|
res.FlagSet = []uint32{flag_valid_pin}
|
||||||
res.FlagReset = []uint32{flag_pin_mismatch}
|
res.FlagReset = []uint32{flag_pin_mismatch}
|
||||||
@@ -320,13 +398,18 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte)
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if len(input) > 0 {
|
firstName := string(input)
|
||||||
if bytes.Equal(input, backOption) {
|
store := h.userdataStore
|
||||||
return res, nil
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
|
if allowUpdate {
|
||||||
|
temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
firstName := string(input)
|
} else {
|
||||||
store := h.userdataStore
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName))
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@@ -343,20 +426,24 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte)
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if len(input) > 0 {
|
store := h.userdataStore
|
||||||
if bytes.Equal(input, backOption) {
|
familyName := string(input)
|
||||||
return res, nil
|
|
||||||
}
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
familyName := string(input)
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
store := h.userdataStore
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName))
|
if allowUpdate {
|
||||||
|
temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return res, fmt.Errorf("a family name cannot be less than one character")
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,10 +455,19 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if len(input) == 4 {
|
yob := string(input)
|
||||||
yob := string(input)
|
store := h.userdataStore
|
||||||
store := h.userdataStore
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob))
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
|
|
||||||
|
if allowUpdate {
|
||||||
|
temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@@ -388,13 +484,20 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) (
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if len(input) > 0 {
|
location := string(input)
|
||||||
if bytes.Equal(input, backOption) {
|
store := h.userdataStore
|
||||||
return res, nil
|
|
||||||
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
|
|
||||||
|
if allowUpdate {
|
||||||
|
temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
location := string(input)
|
} else {
|
||||||
store := h.userdataStore
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location))
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
@@ -412,14 +515,22 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if bytes.Equal(input, backOption) {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
gender := strings.Split(symbol, "_")[1]
|
gender := strings.Split(symbol, "_")[1]
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
if err != nil {
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
return res, nil
|
|
||||||
|
if allowUpdate {
|
||||||
|
temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
@@ -433,12 +544,22 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte)
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
if len(input) > 0 {
|
offerings := string(input)
|
||||||
offerings := string(input)
|
store := h.userdataStore
|
||||||
store := h.userdataStore
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings))
|
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
||||||
|
allowUpdate := h.st.MatchFlag(flag_allow_update, true)
|
||||||
|
|
||||||
|
if allowUpdate {
|
||||||
|
temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE)
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return res, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings))
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,6 +576,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data.
|
||||||
|
func (h *Handlers) ResetValidPin(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)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ResetAccountAuthorized resets the account authorization flag after a successful PIN entry.
|
// ResetAccountAuthorized resets the account authorization flag after a successful PIN entry.
|
||||||
func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
@@ -538,11 +667,13 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b
|
|||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
|
|
||||||
store := h.userdataStore
|
store := h.userdataStore
|
||||||
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
|
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
|
okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.FlagSet = append(res.FlagSet, flag_api_error)
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
||||||
@@ -599,7 +730,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
||||||
|
|
||||||
date := string(input)
|
date := string(input)
|
||||||
_, err = strconv.Atoi(date)
|
_, err = strconv.Atoi(date)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -622,7 +752,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by
|
|||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
|
||||||
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format")
|
||||||
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_incorrect_date_format)
|
res.FlagReset = append(res.FlagReset, flag_incorrect_date_format)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
@@ -705,6 +834,67 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) ResetOthersPin(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")
|
||||||
|
}
|
||||||
|
blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin))
|
||||||
|
if err != nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
|
var res resource.Result
|
||||||
|
flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number")
|
||||||
|
res.FlagReset = append(res.FlagReset, flag_unregistered_number)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handlers) 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")
|
||||||
|
}
|
||||||
|
blockedNumber := string(input)
|
||||||
|
_, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY)
|
||||||
|
if !isValidPhoneNumber(blockedNumber) {
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if db.IsNotFound(err) {
|
||||||
|
logg.Printf(logging.LVL_INFO, "Invalid or unregistered number")
|
||||||
|
res.FlagSet = append(res.FlagSet, flag_unregistered_number)
|
||||||
|
return res, nil
|
||||||
|
} else {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber))
|
||||||
|
if err != nil {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateRecipient validates that the given input is a valid phone number.
|
// ValidateRecipient validates that the given input is a valid phone number.
|
||||||
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
@@ -876,6 +1066,22 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RetrieveBlockedNumber gets the current number during the pin reset for other's is in progress.
|
||||||
|
func (h *Handlers) RetrieveBlockedNumber(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")
|
||||||
|
}
|
||||||
|
store := h.userdataStore
|
||||||
|
blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER)
|
||||||
|
|
||||||
|
res.Content = string(blockedNumber)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetSender returns the sessionId (phoneNumber)
|
// GetSender returns the sessionId (phoneNumber)
|
||||||
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
@@ -1042,13 +1248,13 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by
|
|||||||
if db.IsNotFound(err) {
|
if db.IsNotFound(err) {
|
||||||
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
|
publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch vouchers from the API using the public key
|
// Fetch vouchers from the API using the public key
|
||||||
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
|
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if there is no voucher
|
// Return if there is no voucher
|
||||||
@@ -1105,54 +1311,33 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// process voucher data
|
data := utils.ProcessVouchers(vouchersResp.Result.Holdings)
|
||||||
voucherSymbolList, voucherBalanceList := ProcessVouchers(vouchersResp.Result.Holdings)
|
|
||||||
|
|
||||||
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
|
// Store all voucher data
|
||||||
err = prefixdb.Put(ctx, []byte("sym"), []byte(voucherSymbolList))
|
dataMap := map[string]string{
|
||||||
if err != nil {
|
"sym": data.Symbols,
|
||||||
return res, nil
|
"bal": data.Balances,
|
||||||
|
"deci": data.Decimals,
|
||||||
|
"addr": data.Addresses,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList))
|
for key, value := range dataMap {
|
||||||
if err != nil {
|
if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil {
|
||||||
return res, nil
|
return res, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessVouchers formats the holdings into symbol and balance lists.
|
|
||||||
func ProcessVouchers(holdings []struct {
|
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
|
||||||
Balance string `json:"balance"`
|
|
||||||
}) (string, string) {
|
|
||||||
var numberedSymbols, numberedBalances []string
|
|
||||||
|
|
||||||
for i, voucher := range holdings {
|
|
||||||
numberedSymbols = append(numberedSymbols, fmt.Sprintf("%d:%s", i+1, voucher.TokenSymbol))
|
|
||||||
numberedBalances = append(numberedBalances, fmt.Sprintf("%d:%s", i+1, voucher.Balance))
|
|
||||||
}
|
|
||||||
|
|
||||||
voucherSymbolList := strings.Join(numberedSymbols, "\n")
|
|
||||||
voucherBalanceList := strings.Join(numberedBalances, "\n")
|
|
||||||
|
|
||||||
return voucherSymbolList, voucherBalanceList
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVoucherList fetches the list of vouchers and formats them
|
// GetVoucherList fetches the list of vouchers and formats them
|
||||||
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
|
||||||
// Read vouchers from the store
|
// Read vouchers from the store
|
||||||
store := h.userdataStore
|
voucherData, err := h.prefixDb.Get(ctx, []byte("sym"))
|
||||||
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
|
|
||||||
|
|
||||||
voucherData, err := prefixdb.Get(ctx, []byte("sym"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, nil
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res.Content = string(voucherData)
|
res.Content = string(voucherData)
|
||||||
@@ -1163,8 +1348,6 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte)
|
|||||||
// ViewVoucher retrieves the token holding and balance from the subprefixDB
|
// ViewVoucher retrieves the token holding and balance from the subprefixDB
|
||||||
func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
store := h.userdataStore
|
|
||||||
|
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
@@ -1173,126 +1356,51 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
|
|||||||
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
|
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
|
||||||
|
|
||||||
inputStr := string(input)
|
inputStr := string(input)
|
||||||
|
|
||||||
if inputStr == "0" || inputStr == "99" {
|
if inputStr == "0" || inputStr == "99" {
|
||||||
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
|
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixdb := storage.NewSubPrefixDb(store, []byte("vouchers"))
|
metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr)
|
||||||
|
|
||||||
// Retrieve the voucher symbol list
|
|
||||||
voucherSymbolList, err := prefixdb.Get(ctx, []byte("sym"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, fmt.Errorf("failed to retrieve voucher symbol list: %v", err)
|
return res, fmt.Errorf("failed to retrieve voucher data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the voucher balance list
|
if metadata == nil {
|
||||||
voucherBalanceList, err := prefixdb.Get(ctx, []byte("bal"))
|
|
||||||
if err != nil {
|
|
||||||
return res, fmt.Errorf("failed to retrieve voucher balance list: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// match the voucher symbol and balance with the input
|
|
||||||
matchedSymbol, matchedBalance := MatchVoucher(inputStr, string(voucherSymbolList), string(voucherBalanceList))
|
|
||||||
|
|
||||||
// If a match is found, write the temporary sym and balance
|
|
||||||
if matchedSymbol != "" && matchedBalance != "" {
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol))
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance))
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
|
|
||||||
res.Content = fmt.Sprintf("%s\n%s", matchedSymbol, matchedBalance)
|
|
||||||
} else {
|
|
||||||
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
|
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
|
||||||
|
res.Content = fmt.Sprintf("%s\n%s", metadata.TokenSymbol, metadata.Balance)
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchVoucher finds the matching voucher symbol and balance based on the input.
|
// SetVoucher retrieves the temp voucher data and sets it as the active data
|
||||||
func MatchVoucher(inputStr string, voucherSymbols, voucherBalances string) (string, string) {
|
|
||||||
// Split the lists into slices for processing
|
|
||||||
symbols := strings.Split(voucherSymbols, "\n")
|
|
||||||
balances := strings.Split(voucherBalances, "\n")
|
|
||||||
|
|
||||||
var matchedSymbol, matchedBalance string
|
|
||||||
|
|
||||||
for i, symbol := range symbols {
|
|
||||||
symbolParts := strings.SplitN(symbol, ":", 2)
|
|
||||||
if len(symbolParts) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
voucherNum := symbolParts[0]
|
|
||||||
voucherSymbol := symbolParts[1]
|
|
||||||
|
|
||||||
// Check if input matches either the number or the symbol
|
|
||||||
if inputStr == voucherNum || strings.EqualFold(inputStr, voucherSymbol) {
|
|
||||||
matchedSymbol = voucherSymbol
|
|
||||||
// Ensure there's a corresponding balance
|
|
||||||
if i < len(balances) {
|
|
||||||
matchedBalance = strings.SplitN(balances[i], ":", 2)[1]
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchedSymbol, matchedBalance
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVoucher retrieves the temporary sym and balance,
|
|
||||||
// sets them as the active data and
|
|
||||||
// clears the temporary data
|
|
||||||
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
var err error
|
|
||||||
store := h.userdataStore
|
|
||||||
|
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the current temporary symbol
|
// Get temporary data
|
||||||
temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM)
|
tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
// get the current temporary balance
|
|
||||||
temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the active symbol
|
// Set as active and clear temporary data
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym))
|
if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil {
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
// set the active balance
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(temporaryBal))
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset the temporary symbol
|
res.Content = tempData.TokenSymbol
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(""))
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
// reset the temporary balance
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(""))
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Content = string(temporarySym)
|
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,14 @@ import (
|
|||||||
"git.grassecon.net/urdt/ussd/internal/testutil/mocks"
|
"git.grassecon.net/urdt/ussd/internal/testutil/mocks"
|
||||||
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
|
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/common"
|
||||||
"git.grassecon.net/urdt/ussd/internal/utils"
|
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||||
"github.com/alecthomas/assert/v2"
|
"github.com/alecthomas/assert/v2"
|
||||||
"github.com/grassrootseconomics/eth-custodial/pkg/api"
|
"github.com/grassrootseconomics/eth-custodial/pkg/api"
|
||||||
testdataloader "github.com/peteole/testdata-loader"
|
testdataloader "github.com/peteole/testdata-loader"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -39,7 +42,7 @@ func TestNewHandlers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Run("Valid UserDataStore", func(t *testing.T) {
|
t.Run("Valid UserDataStore", func(t *testing.T) {
|
||||||
mockStore := &mocks.MockUserDataStore{}
|
mockStore := &mocks.MockUserDataStore{}
|
||||||
handlers, err := NewHandlers(fm.parser, mockStore, &accountService)
|
handlers, err := NewHandlers(fm.parser, mockStore, nil, &accountService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
@@ -55,7 +58,7 @@ func TestNewHandlers(t *testing.T) {
|
|||||||
t.Run("Nil UserDataStore", func(t *testing.T) {
|
t.Run("Nil UserDataStore", func(t *testing.T) {
|
||||||
appFlags := &asm.FlagParser{}
|
appFlags := &asm.FlagParser{}
|
||||||
|
|
||||||
handlers, err := NewHandlers(appFlags, nil, &accountService)
|
handlers, err := NewHandlers(appFlags, nil, nil, &accountService)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("expected an error, got none")
|
t.Fatal("expected an error, got none")
|
||||||
@@ -96,7 +99,7 @@ func TestCreateAccount(t *testing.T) {
|
|||||||
Description: "Account creation successed",
|
Description: "Account creation successed",
|
||||||
Result: map[string]any{
|
Result: map[string]any{
|
||||||
"trackingId": "1234567890",
|
"trackingId": "1234567890",
|
||||||
"publicKey": "1235QERYU",
|
"publicKey": "0xD3adB33f",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedResult: resource.Result{
|
expectedResult: resource.Result{
|
||||||
@@ -117,9 +120,10 @@ func TestCreateAccount(t *testing.T) {
|
|||||||
flagManager: fm.parser,
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publicKey := tt.serverResponse.Result["publicKey"].(string)
|
||||||
data := map[utils.DataTyp]string{
|
data := map[utils.DataTyp]string{
|
||||||
utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string),
|
utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string),
|
||||||
utils.DATA_PUBLIC_KEY: tt.serverResponse.Result["publicKey"].(string),
|
utils.DATA_PUBLIC_KEY: publicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr)
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr)
|
||||||
@@ -128,6 +132,12 @@ func TestCreateAccount(t *testing.T) {
|
|||||||
for key, value := range data {
|
for key, value := range data {
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil)
|
mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil)
|
||||||
}
|
}
|
||||||
|
publicKeyNormalized, err := common.NormalizeHex(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil)
|
||||||
|
|
||||||
// Call the method you want to test
|
// Call the method you want to test
|
||||||
res, err := h.CreateAccount(ctx, "create_account", []byte("some-input"))
|
res, err := h.CreateAccount(ctx, "create_account", []byte("some-input"))
|
||||||
@@ -164,8 +174,11 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveFirstname(t *testing.T) {
|
func TestSaveFirstname(t *testing.T) {
|
||||||
// Create a new instance of MockMyDataStore
|
// Create new mocks
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
@@ -173,11 +186,13 @@ func TestSaveFirstname(t *testing.T) {
|
|||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil)
|
||||||
|
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
|
flagManager: fm.parser,
|
||||||
|
st: mockState,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -194,6 +209,9 @@ func TestSaveFirstname(t *testing.T) {
|
|||||||
func TestSaveFamilyname(t *testing.T) {
|
func TestSaveFamilyname(t *testing.T) {
|
||||||
// Create a new instance of UserDataStore
|
// Create a new instance of UserDataStore
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
@@ -201,11 +219,13 @@ func TestSaveFamilyname(t *testing.T) {
|
|||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil)
|
||||||
|
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
|
st: mockState,
|
||||||
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -261,7 +281,7 @@ func TestSaveTemporaryPin(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil)
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input)
|
res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input)
|
||||||
@@ -278,8 +298,11 @@ func TestSaveTemporaryPin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveYoB(t *testing.T) {
|
func TestSaveYoB(t *testing.T) {
|
||||||
// Create a new instance of MockMyDataStore
|
// Create new instances
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
@@ -287,11 +310,13 @@ func TestSaveYoB(t *testing.T) {
|
|||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_YOB, []byte(yob)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil)
|
||||||
|
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
|
st: mockState,
|
||||||
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -308,6 +333,9 @@ func TestSaveYoB(t *testing.T) {
|
|||||||
func TestSaveLocation(t *testing.T) {
|
func TestSaveLocation(t *testing.T) {
|
||||||
// Create a new instance of MockMyDataStore
|
// Create a new instance of MockMyDataStore
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
@@ -315,11 +343,13 @@ func TestSaveLocation(t *testing.T) {
|
|||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_LOCATION, []byte(yob)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil)
|
||||||
|
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
|
st: mockState,
|
||||||
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -336,6 +366,9 @@ func TestSaveLocation(t *testing.T) {
|
|||||||
func TestSaveOfferings(t *testing.T) {
|
func TestSaveOfferings(t *testing.T) {
|
||||||
// Create a new instance of MockUserDataStore
|
// Create a new instance of MockUserDataStore
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define test data
|
// Define test data
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
@@ -343,11 +376,13 @@ func TestSaveOfferings(t *testing.T) {
|
|||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil)
|
||||||
|
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
|
st: mockState,
|
||||||
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -362,10 +397,12 @@ func TestSaveOfferings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveGender(t *testing.T) {
|
func TestSaveGender(t *testing.T) {
|
||||||
// Create a new instance of MockMyDataStore
|
// Create a new mock instances
|
||||||
mockStore := new(mocks.MockUserDataStore)
|
mockStore := new(mocks.MockUserDataStore)
|
||||||
mockState := state.NewState(16)
|
mockState := state.NewState(16)
|
||||||
|
|
||||||
|
fm, _ := NewFlagManager(flagsPath)
|
||||||
|
|
||||||
// Define the session ID and context
|
// Define the session ID and context
|
||||||
sessionId := "session123"
|
sessionId := "session123"
|
||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
@@ -405,16 +442,17 @@ func TestSaveGender(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
// Set up expectations for the mock database
|
// Set up expectations for the mock database
|
||||||
if tt.expectCall {
|
if tt.expectCall {
|
||||||
expectedKey := utils.DATA_GENDER
|
expectedKey := utils.DATA_TEMPORARY_VALUE
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil)
|
||||||
} else {
|
} else {
|
||||||
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)).Return(nil)
|
mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil)
|
||||||
}
|
}
|
||||||
mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol)
|
mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol)
|
||||||
// Create the Handlers instance with the mock store
|
// Create the Handlers instance with the mock store
|
||||||
h := &Handlers{
|
h := &Handlers{
|
||||||
userdataStore: mockStore,
|
userdataStore: mockStore,
|
||||||
st: mockState,
|
st: mockState,
|
||||||
|
flagManager: fm.parser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call the method
|
// Call the method
|
||||||
@@ -425,9 +463,9 @@ func TestSaveGender(t *testing.T) {
|
|||||||
|
|
||||||
// Verify expectations
|
// Verify expectations
|
||||||
if tt.expectCall {
|
if tt.expectCall {
|
||||||
mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender))
|
mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender))
|
||||||
} else {
|
} else {
|
||||||
mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender))
|
mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1003,7 +1041,7 @@ func TestVerifyCreatePin(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
typ := utils.DATA_TEMPORARY_PIN
|
typ := utils.DATA_TEMPORARY_VALUE
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
@@ -1481,8 +1519,8 @@ func TestValidateAmount(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Test with invalid amount format",
|
name: "Test with invalid amount format",
|
||||||
input: []byte("0.02ms"),
|
input: []byte("0.02ms"),
|
||||||
activeBal: []byte("5"),
|
activeBal: []byte("5"),
|
||||||
expectedResult: resource.Result{
|
expectedResult: resource.Result{
|
||||||
FlagSet: []uint32{flag_invalid_amount},
|
FlagSet: []uint32{flag_invalid_amount},
|
||||||
@@ -1796,7 +1834,7 @@ func TestConfirmPin(t *testing.T) {
|
|||||||
// Set up the expected behavior of the mock
|
// Set up the expected behavior of the mock
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil)
|
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil)
|
||||||
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil)
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil)
|
||||||
|
|
||||||
//Call the function under test
|
//Call the function under test
|
||||||
res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin)
|
res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin)
|
||||||
@@ -1811,40 +1849,6 @@ func TestConfirmPin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetVoucher(t *testing.T) {
|
|
||||||
mockDataStore := new(mocks.MockUserDataStore)
|
|
||||||
|
|
||||||
sessionId := "session123"
|
|
||||||
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
|
||||||
|
|
||||||
temporarySym := []byte("tempSym")
|
|
||||||
temporaryBal := []byte("tempBal")
|
|
||||||
|
|
||||||
// Set expectations for the mock data store
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return(temporarySym, nil)
|
|
||||||
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return(temporaryBal, nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, temporarySym).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, temporaryBal).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil)
|
|
||||||
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil)
|
|
||||||
|
|
||||||
h := &Handlers{
|
|
||||||
userdataStore: mockDataStore,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the method under test
|
|
||||||
res, err := h.SetVoucher(ctx, "someSym", []byte{})
|
|
||||||
|
|
||||||
// Assert that no errors occurred
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
// Assert that the result content is correct
|
|
||||||
assert.Equal(t, string(temporarySym), res.Content)
|
|
||||||
|
|
||||||
// Assert that expectations were met
|
|
||||||
mockDataStore.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFetchCustodialBalances(t *testing.T) {
|
func TestFetchCustodialBalances(t *testing.T) {
|
||||||
fm, err := NewFlagManager(flagsPath)
|
fm, err := NewFlagManager(flagsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1926,3 +1930,235 @@ func TestFetchCustodialBalances(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetDefaultVoucher(t *testing.T) {
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf(err.Error())
|
||||||
|
}
|
||||||
|
flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher")
|
||||||
|
if err != nil {
|
||||||
|
t.Logf(err.Error())
|
||||||
|
}
|
||||||
|
// Define session ID and mock data
|
||||||
|
sessionId := "session123"
|
||||||
|
publicKey := "0X13242618721"
|
||||||
|
notFoundErr := db.ErrNotFound{}
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
vouchersResp *models.VoucherHoldingResponse
|
||||||
|
expectedResult resource.Result
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Test set default voucher when no active voucher exists",
|
||||||
|
vouchersResp: &models.VoucherHoldingResponse{
|
||||||
|
Ok: true,
|
||||||
|
Description: "Vouchers fetched successfully",
|
||||||
|
Result: models.VoucherResult{
|
||||||
|
Holdings: []dataserviceapi.TokenHoldings{
|
||||||
|
{
|
||||||
|
ContractAddress: "0x123",
|
||||||
|
TokenSymbol: "TOKEN1",
|
||||||
|
TokenDecimals: "18",
|
||||||
|
Balance: "100",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResult: resource.Result{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Test no vouchers available",
|
||||||
|
vouchersResp: &models.VoucherHoldingResponse{
|
||||||
|
Ok: true,
|
||||||
|
Description: "No vouchers available",
|
||||||
|
Result: models.VoucherResult{
|
||||||
|
Holdings: []dataserviceapi.TokenHoldings{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedResult: resource.Result{
|
||||||
|
FlagSet: []uint32{flag_no_active_voucher},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
mockDataStore := new(mocks.MockUserDataStore)
|
||||||
|
mockAccountService := new(mocks.MockAccountService)
|
||||||
|
|
||||||
|
h := &Handlers{
|
||||||
|
userdataStore: mockDataStore,
|
||||||
|
accountService: mockAccountService,
|
||||||
|
flagManager: fm.parser,
|
||||||
|
}
|
||||||
|
|
||||||
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(""), notFoundErr)
|
||||||
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
|
||||||
|
|
||||||
|
mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil)
|
||||||
|
|
||||||
|
if len(tt.vouchersResp.Result.Holdings) > 0 {
|
||||||
|
firstVoucher := tt.vouchersResp.Result.Holdings[0]
|
||||||
|
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(firstVoucher.TokenSymbol)).Return(nil)
|
||||||
|
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(firstVoucher.Balance)).Return(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input"))
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result")
|
||||||
|
|
||||||
|
mockDataStore.AssertExpectations(t)
|
||||||
|
mockAccountService.AssertExpectations(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckVouchers(t *testing.T) {
|
||||||
|
mockDataStore := new(mocks.MockUserDataStore)
|
||||||
|
mockAccountService := new(mocks.MockAccountService)
|
||||||
|
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
||||||
|
|
||||||
|
sessionId := "session123"
|
||||||
|
publicKey := "0X13242618721"
|
||||||
|
|
||||||
|
h := &Handlers{
|
||||||
|
userdataStore: mockDataStore,
|
||||||
|
accountService: mockAccountService,
|
||||||
|
prefixDb: mockSubPrefixDb,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil)
|
||||||
|
|
||||||
|
mockVouchersResponse := &models.VoucherHoldingResponse{}
|
||||||
|
mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{
|
||||||
|
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
|
||||||
|
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil)
|
||||||
|
|
||||||
|
mockSubPrefixDb.On("Put", ctx, []byte("sym"), []byte("1:SRF\n2:MILO")).Return(nil)
|
||||||
|
mockSubPrefixDb.On("Put", ctx, []byte("bal"), []byte("1:100\n2:200")).Return(nil)
|
||||||
|
mockSubPrefixDb.On("Put", ctx, []byte("deci"), []byte("1:6\n2:4")).Return(nil)
|
||||||
|
mockSubPrefixDb.On("Put", ctx, []byte("addr"), []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa")).Return(nil)
|
||||||
|
|
||||||
|
_, err := h.CheckVouchers(ctx, "check_vouchers", []byte(""))
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
mockDataStore.AssertExpectations(t)
|
||||||
|
mockAccountService.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVoucherList(t *testing.T) {
|
||||||
|
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
||||||
|
|
||||||
|
sessionId := "session123"
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
h := &Handlers{
|
||||||
|
prefixDb: mockSubPrefixDb,
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSubPrefixDb.On("Get", ctx, []byte("sym")).Return([]byte("1:SRF\n2:MILO"), nil)
|
||||||
|
|
||||||
|
res, err := h.GetVoucherList(ctx, "", []byte(""))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, res.Content, "1:SRF\n2:MILO")
|
||||||
|
|
||||||
|
mockSubPrefixDb.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestViewVoucher(t *testing.T) {
|
||||||
|
fm, err := NewFlagManager(flagsPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf(err.Error())
|
||||||
|
}
|
||||||
|
mockDataStore := new(mocks.MockUserDataStore)
|
||||||
|
mockSubPrefixDb := new(mocks.MockSubPrefixDb)
|
||||||
|
|
||||||
|
sessionId := "session123"
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
h := &Handlers{
|
||||||
|
userdataStore: mockDataStore,
|
||||||
|
flagManager: fm.parser,
|
||||||
|
prefixDb: mockSubPrefixDb,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define mock voucher data
|
||||||
|
mockVoucherData := map[string]string{
|
||||||
|
"sym": "1:SRF",
|
||||||
|
"bal": "1:100",
|
||||||
|
"deci": "1:6",
|
||||||
|
"addr": "1:0xd4c288865Ce",
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range mockVoucherData {
|
||||||
|
mockSubPrefixDb.On("Get", ctx, []byte(key)).Return([]byte(value), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up expectations for mockDataStore
|
||||||
|
expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce")
|
||||||
|
|
||||||
|
mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil)
|
||||||
|
|
||||||
|
res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Contains(t, res.Content, "SRF\n100")
|
||||||
|
|
||||||
|
mockDataStore.AssertExpectations(t)
|
||||||
|
mockSubPrefixDb.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetVoucher(t *testing.T) {
|
||||||
|
mockDataStore := new(mocks.MockUserDataStore)
|
||||||
|
|
||||||
|
sessionId := "session123"
|
||||||
|
ctx := context.WithValue(context.Background(), "SessionId", sessionId)
|
||||||
|
|
||||||
|
// Define the temporary voucher data
|
||||||
|
tempData := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
TokenDecimals: "6",
|
||||||
|
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9")
|
||||||
|
|
||||||
|
// Define the expected active entries
|
||||||
|
activeEntries := map[utils.DataTyp][]byte{
|
||||||
|
utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol),
|
||||||
|
utils.DATA_ACTIVE_BAL: []byte(tempData.Balance),
|
||||||
|
utils.DATA_ACTIVE_DECIMAL: []byte(tempData.TokenDecimals),
|
||||||
|
utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mocking ReadEntry calls for temporary data retrieval
|
||||||
|
mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil)
|
||||||
|
|
||||||
|
// Mocking WriteEntry calls for setting active data
|
||||||
|
for key, value := range activeEntries {
|
||||||
|
mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := &Handlers{
|
||||||
|
userdataStore: mockDataStore,
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := h.SetVoucher(ctx, "someSym", []byte{})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, string(tempData.TokenSymbol), res.Content)
|
||||||
|
|
||||||
|
mockDataStore.AssertExpectations(t)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
// VoucherHoldingResponse represents a single voucher holding
|
import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
|
|
||||||
type VoucherHoldingResponse struct {
|
type VoucherHoldingResponse struct {
|
||||||
Ok bool `json:"ok"`
|
Ok bool `json:"ok"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Result struct {
|
Result VoucherResult `json:"result"`
|
||||||
Holdings []struct {
|
}
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
// VoucherResult holds the list of token holdings
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
type VoucherResult struct {
|
||||||
Balance string `json:"balance"`
|
Holdings []dataserviceapi.TokenHoldings `json:"holdings"`
|
||||||
} `json:"holdings"`
|
|
||||||
} `json:"result"`
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ const (
|
|||||||
DATATYPE_USERSUB = 64
|
DATATYPE_USERSUB = 64
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PrefixDb interface abstracts the database operations.
|
||||||
|
type PrefixDb interface {
|
||||||
|
Get(ctx context.Context, key []byte) ([]byte, error)
|
||||||
|
Put(ctx context.Context, key []byte, val []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ PrefixDb = (*SubPrefixDb)(nil)
|
||||||
|
|
||||||
type SubPrefixDb struct {
|
type SubPrefixDb struct {
|
||||||
store db.Db
|
store db.Db
|
||||||
pfx []byte
|
pfx []byte
|
||||||
@@ -29,11 +37,7 @@ func (s *SubPrefixDb) toKey(k []byte) []byte {
|
|||||||
func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
|
func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
|
||||||
s.store.SetPrefix(DATATYPE_USERSUB)
|
s.store.SetPrefix(DATATYPE_USERSUB)
|
||||||
key = s.toKey(key)
|
key = s.toKey(key)
|
||||||
v, err := s.store.Get(ctx, key)
|
return s.store.Get(ctx, key)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error {
|
func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
||||||
lhs.SetDataStore(&userDataStore)
|
lhs.SetDataStore(&userDataStore)
|
||||||
lhs.SetPersister(pe)
|
lhs.SetPersister(pe)
|
||||||
|
|
||||||
|
|||||||
21
internal/testutil/mocks/subprefixdbmock.go
Normal file
21
internal/testutil/mocks/subprefixdbmock.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockSubPrefixDb struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) {
|
||||||
|
args := m.Called(ctx, key)
|
||||||
|
return args.Get(0).([]byte), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockSubPrefixDb) Put(ctx context.Context, key, val []byte) error {
|
||||||
|
args := m.Called(ctx, key, val)
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/models"
|
"git.grassecon.net/urdt/ussd/internal/models"
|
||||||
"github.com/grassrootseconomics/eth-custodial/pkg/api"
|
"github.com/grassrootseconomics/eth-custodial/pkg/api"
|
||||||
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestAccountService struct {
|
type TestAccountService struct {
|
||||||
@@ -75,20 +76,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey
|
|||||||
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) {
|
func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) {
|
||||||
return &models.VoucherHoldingResponse{
|
return &models.VoucherHoldingResponse{
|
||||||
Ok: true,
|
Ok: true,
|
||||||
Result: struct {
|
Result: models.VoucherResult{
|
||||||
Holdings []struct {
|
Holdings: []dataserviceapi.TokenHoldings{
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
|
||||||
Balance string `json:"balance"`
|
|
||||||
} `json:"holdings"`
|
|
||||||
}{
|
|
||||||
Holdings: []struct {
|
|
||||||
ContractAddress string `json:"contractAddress"`
|
|
||||||
TokenSymbol string `json:"tokenSymbol"`
|
|
||||||
TokenDecimals string `json:"tokenDecimals"`
|
|
||||||
Balance string `json:"balance"`
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
|
ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
|
||||||
TokenSymbol: "SRF",
|
TokenSymbol: "SRF",
|
||||||
|
|||||||
51
internal/utils/adminstore.go
Normal file
51
internal/utils/adminstore.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/db"
|
||||||
|
fsdb "git.defalsify.org/vise.git/db/fs"
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("adminstore")
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdminStore struct {
|
||||||
|
ctx context.Context
|
||||||
|
FsStore db.Db
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) {
|
||||||
|
fsStore, err := getFsStore(ctx, fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &AdminStore{ctx: ctx, FsStore: fsStore}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFsStore(ctx context.Context, connectStr string) (db.Db, error) {
|
||||||
|
fsStore := fsdb.NewFsDb()
|
||||||
|
err := fsStore.Connect(ctx, connectStr)
|
||||||
|
fsStore.SetPrefix(db.DATATYPE_USERDATA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return fsStore, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the given sessionId is listed as an admin.
|
||||||
|
func (as *AdminStore) IsAdmin(sessionId string) (bool, error) {
|
||||||
|
_, err := as.FsStore.Get(as.ctx, []byte(sessionId))
|
||||||
|
if err != nil {
|
||||||
|
if db.IsNotFound(err) {
|
||||||
|
logg.Printf(logging.LVL_INFO, "Returning false because session id was not found")
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
@@ -22,12 +22,15 @@ const (
|
|||||||
DATA_OFFERINGS
|
DATA_OFFERINGS
|
||||||
DATA_RECIPIENT
|
DATA_RECIPIENT
|
||||||
DATA_AMOUNT
|
DATA_AMOUNT
|
||||||
DATA_TEMPORARY_PIN
|
DATA_TEMPORARY_VALUE
|
||||||
DATA_VOUCHER_LIST
|
DATA_VOUCHER_LIST
|
||||||
DATA_TEMPORARY_SYM
|
|
||||||
DATA_ACTIVE_SYM
|
DATA_ACTIVE_SYM
|
||||||
DATA_TEMPORARY_BAL
|
|
||||||
DATA_ACTIVE_BAL
|
DATA_ACTIVE_BAL
|
||||||
|
DATA_BLOCKED_NUMBER
|
||||||
|
DATA_PUBLIC_KEY_REVERSE
|
||||||
|
DATA_ACTIVE_DECIMAL
|
||||||
|
DATA_ACTIVE_ADDRESS
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func typToBytes(typ DataTyp) []byte {
|
func typToBytes(typ DataTyp) []byte {
|
||||||
|
|||||||
146
internal/utils/vouchers.go
Normal file
146
internal/utils/vouchers.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VoucherMetadata helps organize data fields
|
||||||
|
type VoucherMetadata struct {
|
||||||
|
Symbols string
|
||||||
|
Balances string
|
||||||
|
Decimals string
|
||||||
|
Addresses string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessVouchers converts holdings into formatted strings
|
||||||
|
func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata {
|
||||||
|
var data VoucherMetadata
|
||||||
|
var symbols, balances, decimals, addresses []string
|
||||||
|
|
||||||
|
for i, h := range holdings {
|
||||||
|
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
|
||||||
|
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
|
||||||
|
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
|
||||||
|
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Symbols = strings.Join(symbols, "\n")
|
||||||
|
data.Balances = strings.Join(balances, "\n")
|
||||||
|
data.Decimals = strings.Join(decimals, "\n")
|
||||||
|
data.Addresses = strings.Join(addresses, "\n")
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetVoucherData retrieves and matches voucher data
|
||||||
|
func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) {
|
||||||
|
keys := []string{"sym", "bal", "deci", "addr"}
|
||||||
|
data := make(map[string]string)
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
value, err := db.Get(ctx, []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get %s: %v", key, err)
|
||||||
|
}
|
||||||
|
data[key] = string(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol, balance, decimal, address := MatchVoucher(input,
|
||||||
|
data["sym"],
|
||||||
|
data["bal"],
|
||||||
|
data["deci"],
|
||||||
|
data["addr"])
|
||||||
|
|
||||||
|
if symbol == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: string(symbol),
|
||||||
|
Balance: string(balance),
|
||||||
|
TokenDecimals: string(decimal),
|
||||||
|
ContractAddress: string(address),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
|
||||||
|
func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
|
||||||
|
symList := strings.Split(symbols, "\n")
|
||||||
|
balList := strings.Split(balances, "\n")
|
||||||
|
decList := strings.Split(decimals, "\n")
|
||||||
|
addrList := strings.Split(addresses, "\n")
|
||||||
|
|
||||||
|
for i, sym := range symList {
|
||||||
|
parts := strings.SplitN(sym, ":", 2)
|
||||||
|
|
||||||
|
if input == parts[0] || strings.EqualFold(input, parts[1]) {
|
||||||
|
symbol = parts[1]
|
||||||
|
if i < len(balList) {
|
||||||
|
balance = strings.SplitN(balList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
if i < len(decList) {
|
||||||
|
decimal = strings.SplitN(decList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
if i < len(addrList) {
|
||||||
|
address = strings.SplitN(addrList[i], ":", 2)[1]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore.
|
||||||
|
func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error {
|
||||||
|
tempData := fmt.Sprintf("%s,%s,%s,%s", data.TokenSymbol, data.Balance, data.TokenDecimals, data.ContractAddress)
|
||||||
|
|
||||||
|
if err := store.WriteEntry(ctx, sessionId, DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore.
|
||||||
|
func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) {
|
||||||
|
temp_data, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values := strings.SplitN(string(temp_data), ",", 4)
|
||||||
|
|
||||||
|
data := &dataserviceapi.TokenHoldings{}
|
||||||
|
|
||||||
|
data.TokenSymbol = values[0]
|
||||||
|
data.Balance = values[1]
|
||||||
|
data.TokenDecimals = values[2]
|
||||||
|
data.ContractAddress = values[3]
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVoucherData sets the active voucher data in the DataStore.
|
||||||
|
func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error {
|
||||||
|
// Active voucher data entries
|
||||||
|
activeEntries := map[DataTyp][]byte{
|
||||||
|
DATA_ACTIVE_SYM: []byte(data.TokenSymbol),
|
||||||
|
DATA_ACTIVE_BAL: []byte(data.Balance),
|
||||||
|
DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals),
|
||||||
|
DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write active data
|
||||||
|
for key, value := range activeEntries {
|
||||||
|
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
198
internal/utils/vouchers_test.go
Normal file
198
internal/utils/vouchers_test.go
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
"github.com/alecthomas/assert/v2"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
memdb "git.defalsify.org/vise.git/db/mem"
|
||||||
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitializeTestDb sets up and returns an in-memory database and store.
|
||||||
|
func InitializeTestDb(t *testing.T) (context.Context, *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
|
||||||
|
store := &UserDataStore{Db: db}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
db.Close() // Ensure the DB is closed after each test
|
||||||
|
})
|
||||||
|
|
||||||
|
return ctx, store
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchVoucher(t *testing.T) {
|
||||||
|
symbols := "1:SRF\n2:MILO"
|
||||||
|
balances := "1:100\n2:200"
|
||||||
|
decimals := "1:6\n2:4"
|
||||||
|
addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa"
|
||||||
|
|
||||||
|
// Test for valid voucher
|
||||||
|
symbol, balance, decimal, address := MatchVoucher("2", symbols, balances, decimals, addresses)
|
||||||
|
|
||||||
|
// Assertions for valid voucher
|
||||||
|
assert.Equal(t, "MILO", symbol)
|
||||||
|
assert.Equal(t, "200", balance)
|
||||||
|
assert.Equal(t, "4", decimal)
|
||||||
|
assert.Equal(t, "0x41c188d63Qa", address)
|
||||||
|
|
||||||
|
// Test for non-existent voucher
|
||||||
|
symbol, balance, decimal, address = MatchVoucher("3", symbols, balances, decimals, addresses)
|
||||||
|
|
||||||
|
// Assertions for non-match
|
||||||
|
assert.Equal(t, "", symbol)
|
||||||
|
assert.Equal(t, "", balance)
|
||||||
|
assert.Equal(t, "", decimal)
|
||||||
|
assert.Equal(t, "", address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVouchers(t *testing.T) {
|
||||||
|
holdings := []dataserviceapi.TokenHoldings{
|
||||||
|
{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"},
|
||||||
|
{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedResult := VoucherMetadata{
|
||||||
|
Symbols: "1:SRF\n2:MILO",
|
||||||
|
Balances: "1:100\n2:200",
|
||||||
|
Decimals: "1:6\n2:4",
|
||||||
|
Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ProcessVouchers(holdings)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedResult, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetVoucherData(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
db := memdb.NewMemDb()
|
||||||
|
err := db.Connect(ctx, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
spdb := storage.NewSubPrefixDb(db, []byte("vouchers"))
|
||||||
|
|
||||||
|
// Test voucher data
|
||||||
|
mockData := map[string][]byte{
|
||||||
|
"sym": []byte("1:SRF\n2:MILO"),
|
||||||
|
"bal": []byte("1:100\n2:200"),
|
||||||
|
"deci": []byte("1:6\n2:4"),
|
||||||
|
"addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put the data
|
||||||
|
for key, value := range mockData {
|
||||||
|
err = spdb.Put(ctx, []byte(key), []byte(value))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := GetVoucherData(ctx, spdb, "1")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "SRF", result.TokenSymbol)
|
||||||
|
assert.Equal(t, "100", result.Balance)
|
||||||
|
assert.Equal(t, "6", result.TokenDecimals)
|
||||||
|
assert.Equal(t, "0xd4c288865Ce", result.ContractAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreTemporaryVoucher(t *testing.T) {
|
||||||
|
ctx, store := InitializeTestDb(t)
|
||||||
|
sessionId := "session123"
|
||||||
|
|
||||||
|
// Test data
|
||||||
|
voucherData := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
TokenDecimals: "6",
|
||||||
|
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the function being tested
|
||||||
|
err := StoreTemporaryVoucher(ctx, store, sessionId, voucherData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify stored data
|
||||||
|
expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9")
|
||||||
|
|
||||||
|
storedValue, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedData, string(storedValue), "Mismatch for key %v", DATA_TEMPORARY_VALUE)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetTemporaryVoucherData(t *testing.T) {
|
||||||
|
ctx, store := InitializeTestDb(t)
|
||||||
|
sessionId := "session123"
|
||||||
|
|
||||||
|
// Test voucher data
|
||||||
|
tempData := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
TokenDecimals: "6",
|
||||||
|
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the data
|
||||||
|
err := StoreTemporaryVoucher(ctx, store, sessionId, tempData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Execute the function being tested
|
||||||
|
data, err := GetTemporaryVoucherData(ctx, store, sessionId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tempData, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateVoucherData(t *testing.T) {
|
||||||
|
ctx, store := InitializeTestDb(t)
|
||||||
|
sessionId := "session123"
|
||||||
|
|
||||||
|
// New voucher data
|
||||||
|
newData := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: "SRF",
|
||||||
|
Balance: "200",
|
||||||
|
TokenDecimals: "6",
|
||||||
|
ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old temporary data
|
||||||
|
tempData := &dataserviceapi.TokenHoldings{
|
||||||
|
TokenSymbol: "OLD",
|
||||||
|
Balance: "100",
|
||||||
|
TokenDecimals: "8",
|
||||||
|
ContractAddress: "0xold",
|
||||||
|
}
|
||||||
|
require.NoError(t, StoreTemporaryVoucher(ctx, store, sessionId, tempData))
|
||||||
|
|
||||||
|
// Execute update
|
||||||
|
err := UpdateVoucherData(ctx, store, sessionId, newData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify active data was stored correctly
|
||||||
|
activeEntries := map[DataTyp][]byte{
|
||||||
|
DATA_ACTIVE_SYM: []byte(newData.TokenSymbol),
|
||||||
|
DATA_ACTIVE_BAL: []byte(newData.Balance),
|
||||||
|
DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals),
|
||||||
|
DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, expectedValue := range activeEntries {
|
||||||
|
storedValue, err := store.ReadEntry(ctx, sessionId, key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, expectedValue, storedValue, "Active data mismatch for key %v", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "5",
|
"input": "5",
|
||||||
"expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back"
|
"expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n0:Back"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input": "1",
|
"input": "1",
|
||||||
|
|||||||
1
services/registration/confirm_others_new_pin
Normal file
1
services/registration/confirm_others_new_pin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Please confirm new PIN for:{{.retrieve_blocked_number}}
|
||||||
14
services/registration/confirm_others_new_pin.vis
Normal file
14
services/registration/confirm_others_new_pin.vis
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
CATCH pin_entry 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 0
|
||||||
|
RELOAD check_pin_mismatch
|
||||||
|
CATCH others_pin_mismatch flag_pin_mismatch 1
|
||||||
|
INCMP pin_entry *
|
||||||
@@ -3,5 +3,3 @@ MOUT back 0
|
|||||||
HALT
|
HALT
|
||||||
INCMP _ 0
|
INCMP _ 0
|
||||||
INCMP * pin_reset_success
|
INCMP * pin_reset_success
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_familyname flag_allow_update 1
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
LOAD save_familyname 0
|
LOAD save_familyname 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_location flag_allow_update 1
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
LOAD save_location 0
|
LOAD save_location 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_firstname flag_allow_update 1
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
LOAD save_firstname 0
|
LOAD save_firstname 0
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_offerings flag_allow_update 1
|
||||||
LOAD save_offerings 0
|
LOAD save_offerings 0
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
|
|||||||
1
services/registration/enter_other_number
Normal file
1
services/registration/enter_other_number
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Enter other's phone number:
|
||||||
7
services/registration/enter_other_number.vis
Normal file
7
services/registration/enter_other_number.vis
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
CATCH no_admin_privilege flag_admin_privilege 0
|
||||||
|
LOAD reset_account_authorized 0
|
||||||
|
RELOAD reset_account_authorized
|
||||||
|
MOUT back 0
|
||||||
|
HALT
|
||||||
|
INCMP _ 0
|
||||||
|
INCMP enter_others_new_pin *
|
||||||
1
services/registration/enter_others_new_pin
Normal file
1
services/registration/enter_others_new_pin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Please enter new PIN for: {{.retrieve_blocked_number}}
|
||||||
12
services/registration/enter_others_new_pin.vis
Normal file
12
services/registration/enter_others_new_pin.vis
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
LOAD validate_blocked_number 6
|
||||||
|
RELOAD validate_blocked_number
|
||||||
|
CATCH unregistered_number flag_unregistered_number 1
|
||||||
|
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
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_yob flag_allow_update 1
|
||||||
LOAD save_yob 0
|
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
LOAD verify_yob 0
|
LOAD verify_yob 0
|
||||||
CATCH incorrect_date_format flag_incorrect_date_format 1
|
CATCH incorrect_date_format flag_incorrect_date_format 1
|
||||||
|
LOAD save_yob 0
|
||||||
RELOAD save_yob
|
RELOAD save_yob
|
||||||
INCMP _ 0
|
INCMP _ 0
|
||||||
INCMP pin_entry *
|
INCMP pin_entry *
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
Guard my PIN
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Linda PIN yangu
|
|
||||||
1
services/registration/invalid_others_pin
Normal file
1
services/registration/invalid_others_pin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The PIN you have entered is invalid.Please try a 4 digit number instead.
|
||||||
5
services/registration/invalid_others_pin.vis
Normal file
5
services/registration/invalid_others_pin.vis
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MOUT retry 1
|
||||||
|
MOUT quit 9
|
||||||
|
HALT
|
||||||
|
INCMP enter_others_new_pin 1
|
||||||
|
INCMP quit 9
|
||||||
1
services/registration/no_admin_privilege
Normal file
1
services/registration/no_admin_privilege
Normal file
@@ -0,0 +1 @@
|
|||||||
|
You do not have privileges to perform this action
|
||||||
5
services/registration/no_admin_privilege.vis
Normal file
5
services/registration/no_admin_privilege.vis
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MOUT quit 9
|
||||||
|
MOUT back 0
|
||||||
|
HALT
|
||||||
|
INCMP pin_management 0
|
||||||
|
INCMP quit 9
|
||||||
1
services/registration/others_pin_mismatch
Normal file
1
services/registration/others_pin_mismatch
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The PIN you have entered is not a match
|
||||||
5
services/registration/others_pin_mismatch.vis
Normal file
5
services/registration/others_pin_mismatch.vis
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
MOUT retry 1
|
||||||
|
MOUT quit 9
|
||||||
|
HALT
|
||||||
|
INCMP _ 1
|
||||||
|
INCMP quit 9
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
MOUT change_pin 1
|
MOUT change_pin 1
|
||||||
MOUT reset_pin 2
|
MOUT reset_pin 2
|
||||||
MOUT guard_pin 3
|
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
INCMP _ 0
|
INCMP my_account 0
|
||||||
INCMP old_pin 1
|
INCMP old_pin 1
|
||||||
|
INCMP enter_other_number 2
|
||||||
|
INCMP . *
|
||||||
|
|||||||
1
services/registration/pin_reset_result
Normal file
1
services/registration/pin_reset_result
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PIN reset request for {{.retrieve_blocked_number}} was successful
|
||||||
8
services/registration/pin_reset_result.vis
Normal file
8
services/registration/pin_reset_result.vis
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
LOAD retrieve_blocked_number 0
|
||||||
|
MAP retrieve_blocked_number
|
||||||
|
LOAD reset_others_pin 6
|
||||||
|
MOUT back 0
|
||||||
|
MOUT quit 9
|
||||||
|
HALT
|
||||||
|
INCMP pin_management 0
|
||||||
|
INCMP quit 9
|
||||||
@@ -6,5 +6,3 @@ MOUT quit 9
|
|||||||
HALT
|
HALT
|
||||||
INCMP main 0
|
INCMP main 0
|
||||||
INCMP quit 9
|
INCMP quit 9
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,3 +17,5 @@ flag,flag_incorrect_date_format,23,this is set when the given year of birth is i
|
|||||||
flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid
|
flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid
|
||||||
flag,flag_api_call_error,25,this is set when communication to an external service fails
|
flag,flag_api_call_error,25,this is set when communication to an external service fails
|
||||||
flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher
|
flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher
|
||||||
|
flag,flag_admin_privilege,27,this is set when a user has admin privileges.
|
||||||
|
flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action
|
||||||
|
|||||||
|
@@ -1,4 +1,4 @@
|
|||||||
LOAD save_gender 0
|
LOAD save_gender 0
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_gender flag_allow_update 1
|
||||||
MOVE pin_entry
|
MOVE pin_entry
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
LOAD save_gender 0
|
LOAD save_gender 0
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_gender flag_allow_update 1
|
||||||
MOVE pin_entry
|
MOVE pin_entry
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
LOAD save_gender 0
|
LOAD save_gender 0
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH update_gender flag_allow_update 1
|
||||||
MOVE pin_entry
|
MOVE pin_entry
|
||||||
|
|||||||
1
services/registration/unregistered_number
Normal file
1
services/registration/unregistered_number
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The number you have entered is either not registered with Sarafu or is invalid.
|
||||||
7
services/registration/unregistered_number.vis
Normal file
7
services/registration/unregistered_number.vis
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
LOAD reset_unregistered_number 0
|
||||||
|
RELOAD reset_unregistered_number
|
||||||
|
MOUT back 0
|
||||||
|
MOUT quit 9
|
||||||
|
HALT
|
||||||
|
INCMP ^ 0
|
||||||
|
INCMP quit 9
|
||||||
2
services/registration/update_age
Normal file
2
services/registration/update_age
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_yob
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_familyname.vis
Normal file
2
services/registration/update_familyname.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_familyname
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_firstname.vis
Normal file
2
services/registration/update_firstname.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_firstname
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_gender.vis
Normal file
2
services/registration/update_gender.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_gender
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_location.vis
Normal file
2
services/registration/update_location.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_location
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_offerings.vis
Normal file
2
services/registration/update_offerings.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_offerings
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
2
services/registration/update_yob.vis
Normal file
2
services/registration/update_yob.vis
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
RELOAD save_yob
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
Reference in New Issue
Block a user