forked from grassrootseconomics/visedriver
		
	Merge branch 'master' into lash/store-dumper
This commit is contained in:
		
						commit
						5937c6bf5c
					
				| @ -19,9 +19,9 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -131,7 +131,7 @@ func main() { | ||||
| 		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) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| @ -139,7 +139,7 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -16,8 +16,8 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -104,9 +104,9 @@ func main() { | ||||
| 		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) | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 
 | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
|  | ||||
| @ -18,9 +18,9 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	httpserver "git.grassecon.net/urdt/ussd/internal/http" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -92,14 +92,15 @@ func main() { | ||||
| 		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) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	accountService := server.AccountService{} | ||||
| 
 | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -13,8 +13,8 @@ import ( | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -88,7 +88,7 @@ func main() { | ||||
| 		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.SetPersister(pe) | ||||
| 
 | ||||
| @ -97,7 +97,7 @@ func main() { | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	accountService := server.AccountService{} | ||||
| 	accountService := remote.AccountService{} | ||||
| 	hl, err := lhs.GetHandler(&accountService) | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(os.Stderr, err.Error()) | ||||
|  | ||||
| @ -1,7 +1,9 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/binary" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| ) | ||||
| 
 | ||||
| type DataTyp uint16 | ||||
| @ -23,13 +25,17 @@ const ( | ||||
| 	DATA_RECIPIENT | ||||
| 	DATA_AMOUNT | ||||
| 	DATA_TEMPORARY_VALUE | ||||
| 	DATA_VOUCHER_LIST | ||||
| 	DATA_ACTIVE_SYM | ||||
| 	DATA_ACTIVE_BAL | ||||
| 	DATA_BLOCKED_NUMBER | ||||
| 	DATA_PUBLIC_KEY_REVERSE | ||||
| 	DATA_ACTIVE_DECIMAL | ||||
| 	DATA_ACTIVE_ADDRESS | ||||
| 	DATA_TRANSACTIONS | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	logg = logging.NewVanilla().WithDomain("urdt-common") | ||||
| ) | ||||
| 
 | ||||
| func typToBytes(typ DataTyp) []byte { | ||||
| @ -2,6 +2,7 @@ package common | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/hex" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func NormalizeHex(s string) (string, error) { | ||||
| @ -16,3 +17,15 @@ func NormalizeHex(s string) (string, error) { | ||||
| 	} | ||||
| 	return hex.EncodeToString(r), nil | ||||
| } | ||||
| 
 | ||||
| func IsSameHex(left string, right string) bool { | ||||
| 	bl, err := NormalizeHex(left) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	br, err := NormalizeHex(left) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return strings.Compare(bl, br) == 0 | ||||
| } | ||||
|  | ||||
							
								
								
									
										52
									
								
								common/storage.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								common/storage.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/db" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| ) | ||||
| 
 | ||||
| func StoreToDb(store *UserDataStore) db.Db { | ||||
| 	return store.Db | ||||
| } | ||||
| 
 | ||||
| func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { | ||||
| 	return storage.NewSubPrefixDb(store.Db, pfx)	 | ||||
| } | ||||
| 
 | ||||
| type StorageServices interface { | ||||
| 	GetPersister(ctx context.Context) (*persist.Persister, error) | ||||
| 	GetUserdataDb(ctx context.Context) (db.Db, error) | ||||
| 	GetResource(ctx context.Context) (resource.Resource, error) | ||||
| 	EnsureDbDir() error | ||||
| } | ||||
| 
 | ||||
| type StorageService struct { | ||||
| 	svc *storage.MenuStorageService | ||||
| } | ||||
| 
 | ||||
| func NewStorageService(dbDir string) *StorageService { | ||||
| 	return &StorageService{ | ||||
| 		svc: storage.NewMenuStorageService(dbDir, ""), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { | ||||
| 	return ss.svc.GetPersister(ctx) | ||||
| } | ||||
| 	 | ||||
| func(ss *StorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { | ||||
| 	return ss.svc.GetUserdataDb(ctx) | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) { | ||||
| 	return nil, errors.New("not implemented") | ||||
| } | ||||
| 
 | ||||
| func(ss *StorageService) EnsureDbDir() error { | ||||
| 	return ss.svc.EnsureDbDir() | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| @ -16,7 +16,7 @@ type UserDataStore struct { | ||||
| 	db.Db | ||||
| } | ||||
| 
 | ||||
| // ReadEntry retrieves an entry from the store based on the provided parameters.
 | ||||
| // ReadEntry retrieves an entry to the userdata store.
 | ||||
| func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| @ -24,6 +24,8 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ | ||||
| 	return store.Get(ctx, k) | ||||
| } | ||||
| 
 | ||||
| // WriteEntry adds an entry to the userdata store.
 | ||||
| // BUG: this uses sessionId twice
 | ||||
| func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { | ||||
| 	store.SetPrefix(db.DATATYPE_USERDATA) | ||||
| 	store.SetSession(sessionId) | ||||
| @ -1,4 +1,4 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| @ -37,6 +37,15 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { | ||||
| 	return data | ||||
| } | ||||
| 
 | ||||
| //func StoreVouchers(db storage.PrefixDb, data VoucherMetadata) {
 | ||||
| //		value, err := db.Put(ctx, []byte(key))
 | ||||
| //		if err != nil {
 | ||||
| //			return nil, fmt.Errorf("failed to get %s: %v", key, err)
 | ||||
| //		}
 | ||||
| //		data[key] = string(value)
 | ||||
| //	}
 | ||||
| //}
 | ||||
| 
 | ||||
| // 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"} | ||||
| @ -75,6 +84,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, | ||||
| 	decList := strings.Split(decimals, "\n") | ||||
| 	addrList := strings.Split(addresses, "\n") | ||||
| 
 | ||||
| 	logg.Tracef("found" , "symlist", symList, "syms", symbols, "input", input) | ||||
| 	for i, sym := range symList { | ||||
| 		parts := strings.SplitN(sym, ":", 2) | ||||
| 
 | ||||
| @ -125,8 +135,9 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str | ||||
| 	return data, nil | ||||
| } | ||||
| 
 | ||||
| // UpdateVoucherData sets the active voucher data in the DataStore.
 | ||||
| // UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore.
 | ||||
| func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { | ||||
| 	logg.TraceCtxf(ctx, "dtal", "data", data) | ||||
| 	// Active voucher data entries
 | ||||
| 	activeEntries := map[DataTyp][]byte{ | ||||
| 		DATA_ACTIVE_SYM:     []byte(data.TokenSymbol), | ||||
| @ -1,14 +1,14 @@ | ||||
| package utils | ||||
| package common | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"github.com/alecthomas/assert/v2" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	memdb "git.defalsify.org/vise.git/db/mem" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| ) | ||||
| @ -132,7 +132,6 @@ func TestStoreTemporaryVoucher(t *testing.T) { | ||||
| 	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) { | ||||
| @ -1,18 +1,70 @@ | ||||
| package config | ||||
| 
 | ||||
| import "git.grassecon.net/urdt/ussd/initializers" | ||||
| import ( | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/initializers" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	createAccountPath = "/api/v2/account/create" | ||||
| 	trackStatusPath = "/api/track" | ||||
| 	balancePathPrefix = "/api/account" | ||||
| 	trackPath = "/api/v2/account/status" | ||||
| 	voucherHoldingsPathPrefix = "/api/v1/holdings" | ||||
| 	voucherTransfersPathPrefix = "/api/v1/transfers/last10" | ||||
| 	voucherDataPathPrefix = "/api/v1/token" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	custodialURLBase string | ||||
| 	dataURLBase string | ||||
| 	CustodialAPIKey string | ||||
| 	DataAPIKey string | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	CreateAccountURL string | ||||
| 	TrackStatusURL   string | ||||
| 	BalanceURL       string | ||||
|     TrackURL         string | ||||
| 	BalanceURL	string | ||||
| 	TrackURL         string | ||||
| 	VoucherHoldingsURL	string | ||||
| 	VoucherTransfersURL	string | ||||
| 	VoucherDataURL	string | ||||
| ) | ||||
| 
 | ||||
| // LoadConfig initializes the configuration values after environment variables are loaded.
 | ||||
| func LoadConfig() { | ||||
| 	CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") | ||||
| 	TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") | ||||
| 	BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") | ||||
|     TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") | ||||
| func setBase() error { | ||||
| 	var err error | ||||
| 
 | ||||
| 	custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") | ||||
| 	dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") | ||||
| 	CustodialAPIKey = initializers.GetEnv("CUSTODIAL_API_KEY", "xd") | ||||
| 	DataAPIKey = initializers.GetEnv("DATA_API_KEY", "xd") | ||||
| 
 | ||||
| 	_, err = url.JoinPath(custodialURLBase, "/foo") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	_, err = url.JoinPath(dataURLBase, "/bar") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // LoadConfig initializes the configuration values after environment variables are loaded.
 | ||||
| func LoadConfig() error { | ||||
| 	err := setBase() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	CreateAccountURL, _  = url.JoinPath(custodialURLBase, createAccountPath) | ||||
| 	TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) | ||||
| 	BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) | ||||
| 	TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) | ||||
| 	VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) | ||||
| 	VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) | ||||
| 	VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) | ||||
| 
 | ||||
| 	return 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) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| @ -1,13 +1,17 @@ | ||||
| package handlers | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.defalsify.org/vise.git/asm" | ||||
| 	"git.defalsify.org/vise.git/db" | ||||
| 	"git.defalsify.org/vise.git/engine" | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/ussd" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| type HandlerService interface { | ||||
| @ -28,20 +32,26 @@ type LocalHandlerService struct { | ||||
| 	DbRs          *resource.DbResource | ||||
| 	Pe            *persist.Persister | ||||
| 	UserdataStore *db.Db | ||||
| 	AdminStore    *utils.AdminStore | ||||
| 	Cfg           engine.Config | ||||
| 	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) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &LocalHandlerService{ | ||||
| 		Parser: parser, | ||||
| 		DbRs:   dbResource, | ||||
| 		Cfg:    cfg, | ||||
| 		Rs:     rs, | ||||
| 		Parser:     parser, | ||||
| 		DbRs:       dbResource, | ||||
| 		AdminStore: adminstore, | ||||
| 		Cfg:        cfg, | ||||
| 		Rs:         rs, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| @ -53,8 +63,8 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { | ||||
| 	ls.UserdataStore = db | ||||
| } | ||||
| 
 | ||||
| func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { | ||||
| 	ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) | ||||
| func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { | ||||
| 	ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -98,6 +108,13 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn | ||||
| 	ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) | ||||
| 	ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) | ||||
| 	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 | ||||
| } | ||||
|  | ||||
| @ -1,185 +0,0 @@ | ||||
| package server | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	okResponse  api.OKResponse | ||||
| 	errResponse api.ErrResponse | ||||
| ) | ||||
| 
 | ||||
| type AccountServiceInterface interface { | ||||
| 	CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) | ||||
| 	CreateAccount(ctx context.Context) (*api.OKResponse, error) | ||||
| 	CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) | ||||
| 	TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) | ||||
| 	FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| // Parameters:
 | ||||
| //   - trackingId: A unique identifier for the account.This should be obtained from a previous call to
 | ||||
| //     CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
 | ||||
| //     AccountResponse struct can be used here to check the account status during a transaction.
 | ||||
| //
 | ||||
| // Returns:
 | ||||
| //   - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil
 | ||||
| func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| 	resp, err := http.Get(config.BalanceURL + trackingId) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	var trackResp models.TrackStatusResponse | ||||
| 	err = json.Unmarshal(body, &trackResp) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &trackResp, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { | ||||
| 	var err error | ||||
| 	// Construct the URL with the path parameter
 | ||||
| 	url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) | ||||
| 	req, err := http.NewRequest("GET", url, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("X-GE-KEY", "xd") | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 	return &okResponse, nil | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account whose balance needs to be checked.
 | ||||
| func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| 	resp, err := http.Get(config.BalanceURL + publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var balanceResp models.BalanceResponse | ||||
| 	err = json.Unmarshal(body, &balanceResp) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &balanceResp, nil | ||||
| } | ||||
| 
 | ||||
| // CreateAccount creates a new account in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account.
 | ||||
| //     If there is an error during the request or processing, this will be nil.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| 	var err error | ||||
| 
 | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.CreateAccountURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	req.Header.Set("X-GE-KEY", "xd") | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 	return &okResponse, nil | ||||
| } | ||||
| 
 | ||||
| // FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| 	file, err := os.Open("sample_tokens.json") | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	var holdings models.VoucherHoldingResponse | ||||
| 
 | ||||
| 	if err := json.NewDecoder(file).Decode(&holdings); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &holdings, nil | ||||
| } | ||||
| @ -20,8 +20,8 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"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/utils" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| 	"gopkg.in/leonelquinteros/gotext.v1" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| @ -35,6 +35,12 @@ var ( | ||||
| 	errResponse    *api.ErrResponse | ||||
| ) | ||||
| 
 | ||||
| // 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
 | ||||
| type FlagManager struct { | ||||
| 	parser *asm.FlagParser | ||||
| @ -62,17 +68,18 @@ type Handlers struct { | ||||
| 	pe             *persist.Persister | ||||
| 	st             *state.State | ||||
| 	ca             cache.Memory | ||||
| 	userdataStore  utils.DataStore | ||||
| 	userdataStore  common.DataStore | ||||
| 	adminstore     *utils.AdminStore | ||||
| 	flagManager    *asm.FlagParser | ||||
| 	accountService server.AccountServiceInterface | ||||
| 	accountService remote.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 remote.AccountServiceInterface) (*Handlers, error) { | ||||
| 	if userdataStore == nil { | ||||
| 		return nil, fmt.Errorf("cannot create handler with nil userdata store") | ||||
| 	} | ||||
| 	userDb := &utils.UserDataStore{ | ||||
| 	userDb := &common.UserDataStore{ | ||||
| 		Db: userdataStore, | ||||
| 	} | ||||
| 	// Instantiate the SubPrefixDb with "vouchers" prefix
 | ||||
| @ -81,21 +88,24 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s | ||||
| 	h := &Handlers{ | ||||
| 		userdataStore:  userDb, | ||||
| 		flagManager:    appFlags, | ||||
| 		adminstore:     adminstore, | ||||
| 		accountService: accountService, | ||||
| 		prefixDb:       prefixDb, | ||||
| 	} | ||||
| 	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
 | ||||
| func isValidPIN(pin string) bool { | ||||
| 	match, _ := regexp.MatchString(pinPattern, pin) | ||||
| 	return match | ||||
| } | ||||
| 
 | ||||
| func isValidPhoneNumber(phonenumber string) bool { | ||||
| 	match, _ := regexp.MatchString(phoneRegex, phonenumber) | ||||
| 	return match | ||||
| } | ||||
| 
 | ||||
| func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { | ||||
| 	if h.pe != nil { | ||||
| 		panic("persister already set") | ||||
| @ -106,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) { | ||||
| 	var r resource.Result | ||||
| 
 | ||||
| 	if h.pe == nil { | ||||
| 		logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca) | ||||
| 		return r, nil | ||||
| 	} | ||||
| 
 | ||||
| 	h.st = h.pe.GetState() | ||||
| 	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 { | ||||
| 		logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) | ||||
| 		return r, fmt.Errorf("cannot get state and memory for handler") | ||||
| @ -148,16 +170,16 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r | ||||
| 
 | ||||
| func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { | ||||
| 	flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") | ||||
| 	okResponse, err := h.accountService.CreateAccount(ctx) | ||||
| 	r, err := h.accountService.CreateAccount(ctx) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	trackingId := okResponse.Result["trackingId"].(string) | ||||
| 	publicKey := okResponse.Result["publicKey"].(string) | ||||
| 	trackingId := r.TrackingId | ||||
| 	publicKey := r.PublicKey | ||||
| 
 | ||||
| 	data := map[utils.DataTyp]string{ | ||||
| 		utils.DATA_TRACKING_ID: trackingId, | ||||
| 		utils.DATA_PUBLIC_KEY:  publicKey, | ||||
| 	data := map[common.DataTyp]string{ | ||||
| 		common.DATA_TRACKING_ID: trackingId, | ||||
| 		common.DATA_PUBLIC_KEY:  publicKey, | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	for key, value := range data { | ||||
| @ -170,7 +192,7 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) | ||||
| 	err = store.WriteEntry(ctx, publicKeyNormalized, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -190,7 +212,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_CREATED) | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") | ||||
| @ -203,6 +225,30 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) | ||||
| 	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, common.DATA_BLOCKED_NUMBER) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.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) { | ||||
| 	res := resource.Result{} | ||||
| 	_, ok := ctx.Value("SessionId").(string) | ||||
| @ -234,7 +280,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt | ||||
| 	} | ||||
| 
 | ||||
| 	flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") | ||||
| 
 | ||||
| 	accountPIN := string(input) | ||||
| 
 | ||||
| 	// Validate that the PIN is a 4-digit number
 | ||||
| @ -242,11 +287,32 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt | ||||
| 		res.FlagSet = append(res.FlagSet, flag_incorrect_pin) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	res.FlagReset = append(res.FlagReset, flag_incorrect_pin) | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.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 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []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, common.DATA_BLOCKED_NUMBER) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -263,7 +329,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt | ||||
| 	flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -272,7 +338,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt | ||||
| 	} else { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_pin_mismatch) | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -294,11 +360,10 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	if bytes.Equal(input, temporaryPin) { | ||||
| 		res.FlagSet = []uint32{flag_valid_pin} | ||||
| 		res.FlagReset = []uint32{flag_pin_mismatch} | ||||
| @ -307,7 +372,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte | ||||
| 		res.FlagSet = []uint32{flag_pin_mismatch} | ||||
| 	} | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -338,13 +403,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) | ||||
| 	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)) | ||||
| 		temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -361,6 +426,7 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	familyName := string(input) | ||||
| 
 | ||||
| @ -368,13 +434,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) | ||||
| 		temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -396,13 +462,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou | ||||
| 	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)) | ||||
| 		temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -426,13 +492,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( | ||||
| 	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)) | ||||
| 		temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -456,13 +522,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re | ||||
| 	allowUpdate := h.st.MatchFlag(flag_allow_update, true) | ||||
| 
 | ||||
| 	if allowUpdate { | ||||
| 		temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) | ||||
| 		temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -479,6 +545,7 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	offerings := string(input) | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| @ -486,13 +553,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) | ||||
| 	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)) | ||||
| 		temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| 	} else { | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) | ||||
| 		if err != nil { | ||||
| 			return res, err | ||||
| 		} | ||||
| @ -511,6 +578,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt | ||||
| 	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.
 | ||||
| func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -528,7 +603,7 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 
 | ||||
| 	res.Content = string(publicKey) | ||||
| 
 | ||||
| @ -549,7 +624,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res | ||||
| 	flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) | ||||
| 	AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -594,22 +669,23 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b | ||||
| 	if !ok { | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) | ||||
| 	r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_api_error) | ||||
| 		return res, err | ||||
| 	} | ||||
| 	res.FlagReset = append(res.FlagReset, flag_api_error) | ||||
| 	isActive := okResponse.Result["active"].(bool) | ||||
| 	if !ok { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	if isActive { | ||||
| 	if r.Active { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_account_success) | ||||
| 		res.FlagReset = append(res.FlagReset, flag_account_pending) | ||||
| 	} else { | ||||
| @ -655,7 +731,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res | ||||
| 	var err error | ||||
| 
 | ||||
| 	flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") | ||||
| 
 | ||||
| 	date := string(input) | ||||
| 	_, err = strconv.Atoi(date) | ||||
| 	if err != nil { | ||||
| @ -678,7 +753,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by | ||||
| 	var res resource.Result | ||||
| 
 | ||||
| 	flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") | ||||
| 
 | ||||
| 	res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) | ||||
| 	return res, nil | ||||
| } | ||||
| @ -701,7 +775,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	// get the active sym and active balance
 | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			balance := "0.00" | ||||
| @ -712,7 +786,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -724,6 +798,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( | ||||
| 
 | ||||
| func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| 
 | ||||
| 	flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") | ||||
| 
 | ||||
| 	sessionId, ok := ctx.Value("SessionId").(string) | ||||
| @ -734,21 +809,19 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input | ||||
| 	balanceType := strings.Split(symbol, "_")[0] | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	if !balanceResponse.Ok { | ||||
| 		res.FlagSet = append(res.FlagSet, flag_api_error) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 	res.FlagReset = append(res.FlagReset, flag_api_error) | ||||
| 	balance := balanceResponse.Result.Balance | ||||
| 
 | ||||
| 	balance := balanceResponse.Balance | ||||
| 
 | ||||
| 	switch balanceType { | ||||
| 	case "my": | ||||
| @ -761,6 +834,67 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input | ||||
| 	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, common.DATA_BLOCKED_NUMBER) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 	err = store.WriteEntry(ctx, string(blockedPhonenumber), common.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, common.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, common.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.
 | ||||
| func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -784,7 +918,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by | ||||
| 			return res, nil | ||||
| 		} | ||||
| 		store := h.userdataStore | ||||
| 		err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recipient)) | ||||
| 		err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) | ||||
| 		if err != nil { | ||||
| 			return res, nil | ||||
| 		} | ||||
| @ -807,12 +941,12 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt | ||||
| 	flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") | ||||
| 	flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -834,7 +968,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input | ||||
| 
 | ||||
| 	flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") | ||||
| 	store := h.userdataStore | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -856,7 +990,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -881,7 +1015,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 	var balanceValue float64 | ||||
| 
 | ||||
| 	// retrieve the active balance
 | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) | ||||
| 	activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -907,7 +1041,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) | ||||
| 
 | ||||
| 	// Format the amount with 2 decimal places before saving
 | ||||
| 	formattedAmount := fmt.Sprintf("%.2f", inputAmount) | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(formattedAmount)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| @ -925,13 +1059,29 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( | ||||
| 		return res, fmt.Errorf("missing session") | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) | ||||
| 
 | ||||
| 	res.Content = string(recipient) | ||||
| 
 | ||||
| 	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, common.DATA_BLOCKED_NUMBER) | ||||
| 
 | ||||
| 	res.Content = string(blockedNumber) | ||||
| 
 | ||||
| 	return res, nil | ||||
| } | ||||
| 
 | ||||
| // GetSender returns the sessionId (phoneNumber)
 | ||||
| func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { | ||||
| 	var res resource.Result | ||||
| @ -957,12 +1107,12 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	// retrieve the active symbol
 | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) | ||||
| 
 | ||||
| 	res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) | ||||
| 
 | ||||
| @ -986,11 +1136,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] | ||||
| 	// Use the amount, recipient and sender to call the API and initialize the transaction
 | ||||
| 	store := h.userdataStore | ||||
| 
 | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) | ||||
| 	amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) | ||||
| 
 | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) | ||||
| 	recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) | ||||
| 
 | ||||
| 	activeSym, _ := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 
 | ||||
| 	res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) | ||||
| 
 | ||||
| @ -1030,12 +1180,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) | ||||
| 	} | ||||
| 	store := h.userdataStore | ||||
| 	// Retrieve user data as strings with fallback to defaultValue
 | ||||
| 	firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME)) | ||||
| 	familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME)) | ||||
| 	yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_YOB)) | ||||
| 	gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_GENDER)) | ||||
| 	location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION)) | ||||
| 	offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS)) | ||||
| 	firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME)) | ||||
| 	familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME)) | ||||
| 	yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_YOB)) | ||||
| 	gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_GENDER)) | ||||
| 	location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_LOCATION)) | ||||
| 	offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS)) | ||||
| 
 | ||||
| 	// Construct the full name
 | ||||
| 	name := defaultValue | ||||
| @ -1092,11 +1242,11 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by | ||||
| 	flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") | ||||
| 
 | ||||
| 	// check if the user has an active sym
 | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) | ||||
| 	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		if db.IsNotFound(err) { | ||||
| 			publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 			publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| @ -1108,23 +1258,23 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by | ||||
| 			} | ||||
| 
 | ||||
| 			// Return if there is no voucher
 | ||||
| 			if len(vouchersResp.Result.Holdings) == 0 { | ||||
| 			if len(vouchersResp) == 0 { | ||||
| 				res.FlagSet = append(res.FlagSet, flag_no_active_voucher) | ||||
| 				return res, nil | ||||
| 			} | ||||
| 
 | ||||
| 			// Use only the first voucher
 | ||||
| 			firstVoucher := vouchersResp.Result.Holdings[0] | ||||
| 			firstVoucher := vouchersResp[0] | ||||
| 			defaultSym := firstVoucher.TokenSymbol | ||||
| 			defaultBal := firstVoucher.Balance | ||||
| 
 | ||||
| 			// set the active symbol
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(defaultSym)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| 			// set the active balance
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(defaultBal)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) | ||||
| 			if err != nil { | ||||
| 				return res, err | ||||
| 			} | ||||
| @ -1150,7 +1300,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) | ||||
| 	} | ||||
| 
 | ||||
| 	store := h.userdataStore | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) | ||||
| 	publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) | ||||
| 	if err != nil { | ||||
| 		return res, nil | ||||
| 	} | ||||
| @ -1161,7 +1311,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	data := utils.ProcessVouchers(vouchersResp.Result.Holdings) | ||||
| 	data := common.ProcessVouchers(vouchersResp) | ||||
| 
 | ||||
| 	// Store all voucher data
 | ||||
| 	dataMap := map[string]string{ | ||||
| @ -1211,7 +1361,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr) | ||||
| 	metadata, err := common.GetVoucherData(ctx, h.prefixDb, inputStr) | ||||
| 	if err != nil { | ||||
| 		return res, fmt.Errorf("failed to retrieve voucher data: %v", err) | ||||
| 	} | ||||
| @ -1221,7 +1371,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r | ||||
| 		return res, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { | ||||
| 	if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| @ -1241,13 +1391,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re | ||||
| 	} | ||||
| 
 | ||||
| 	// Get temporary data
 | ||||
| 	tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) | ||||
| 	tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) | ||||
| 	if err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Set as active and clear temporary data
 | ||||
| 	if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { | ||||
| 	if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { | ||||
| 		return res, err | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -12,14 +12,14 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/persist" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.defalsify.org/vise.git/state" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/mocks" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/testservice" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/utils" | ||||
| 	"git.grassecon.net/urdt/ussd/common" | ||||
| 	"github.com/alecthomas/assert/v2" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 
 | ||||
| 	testdataloader "github.com/peteole/testdata-loader" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 
 | ||||
| @ -33,7 +33,7 @@ var ( | ||||
| ) | ||||
| 
 | ||||
| // InitializeTestStore sets up and returns an in-memory database and store.
 | ||||
| func InitializeTestStore(t *testing.T) (context.Context, *utils.UserDataStore) { | ||||
| func InitializeTestStore(t *testing.T) (context.Context, *common.UserDataStore) { | ||||
| 	ctx := context.Background() | ||||
| 
 | ||||
| 	// Initialize memDb
 | ||||
| @ -42,7 +42,7 @@ func InitializeTestStore(t *testing.T) (context.Context, *utils.UserDataStore) { | ||||
| 	require.NoError(t, err, "Failed to connect to memDb") | ||||
| 
 | ||||
| 	// Create UserDataStore with memDb
 | ||||
| 	store := &utils.UserDataStore{Db: db} | ||||
| 	store := &common.UserDataStore{Db: db} | ||||
| 
 | ||||
| 	t.Cleanup(func() { | ||||
| 		db.Close() // Ensure the DB is closed after each test
 | ||||
| @ -71,7 +71,7 @@ func TestNewHandlers(t *testing.T) { | ||||
| 		t.Logf(err.Error()) | ||||
| 	} | ||||
| 	t.Run("Valid UserDataStore", func(t *testing.T) { | ||||
| 		handlers, err := NewHandlers(fm.parser, store, &accountService) | ||||
| 		handlers, err := NewHandlers(fm.parser, store, nil, &accountService) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("expected no error, got %v", err) | ||||
| 		} | ||||
| @ -85,7 +85,7 @@ func TestNewHandlers(t *testing.T) { | ||||
| 
 | ||||
| 	// Test case for nil userdataStore
 | ||||
| 	t.Run("Nil UserDataStore", func(t *testing.T) { | ||||
| 		handlers, err := NewHandlers(fm.parser, nil, &accountService) | ||||
| 		handlers, err := NewHandlers(fm.parser, nil, nil, &accountService) | ||||
| 		if err == nil { | ||||
| 			t.Fatal("expected an error, got none") | ||||
| 		} | ||||
| @ -115,18 +115,14 @@ func TestCreateAccount(t *testing.T) { | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		serverResponse *api.OKResponse | ||||
| 		serverResponse *models.AccountResult | ||||
| 		expectedResult resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "Test account creation success", | ||||
| 			serverResponse: &api.OKResponse{ | ||||
| 				Ok:          true, | ||||
| 				Description: "Account creation successed", | ||||
| 				Result: map[string]any{ | ||||
| 					"trackingId": "1234567890", | ||||
| 					"publicKey":  "0xD3adB33f", | ||||
| 				}, | ||||
| 			serverResponse: &models.AccountResult{ | ||||
| 				TrackingId: "1234567890", | ||||
| 				PublicKey:  "0xD3adB33f", | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagSet: []uint32{flag_account_created}, | ||||
| @ -180,7 +176,7 @@ func TestSaveFirstname(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -192,7 +188,7 @@ func TestSaveFirstname(t *testing.T) { | ||||
| 	// Define test data
 | ||||
| 	firstName := "John" | ||||
| 
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| @ -211,7 +207,7 @@ func TestSaveFirstname(t *testing.T) { | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Verify that the DATA_FIRST_NAME entry has been updated with the temporary value
 | ||||
| 	storedFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME) | ||||
| 	storedFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME) | ||||
| 	assert.Equal(t, firstName, string(storedFirstName)) | ||||
| } | ||||
| 
 | ||||
| @ -219,7 +215,7 @@ func TestSaveFamilyname(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -231,7 +227,7 @@ func TestSaveFamilyname(t *testing.T) { | ||||
| 	// Define test data
 | ||||
| 	familyName := "Doeee" | ||||
| 
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| @ -250,7 +246,7 @@ func TestSaveFamilyname(t *testing.T) { | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value
 | ||||
| 	storedFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME) | ||||
| 	storedFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) | ||||
| 	assert.Equal(t, familyName, string(storedFamilyName)) | ||||
| } | ||||
| 
 | ||||
| @ -258,7 +254,7 @@ func TestSaveYoB(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -269,8 +265,8 @@ func TestSaveYoB(t *testing.T) { | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	yob := "1980" | ||||
| 	 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { | ||||
| 
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| @ -289,7 +285,7 @@ func TestSaveYoB(t *testing.T) { | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Verify that the DATA_YOB entry has been updated with the temporary value
 | ||||
| 	storedYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_YOB) | ||||
| 	storedYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_YOB) | ||||
| 	assert.Equal(t, yob, string(storedYob)) | ||||
| } | ||||
| 
 | ||||
| @ -297,7 +293,7 @@ func TestSaveLocation(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -308,8 +304,8 @@ func TestSaveLocation(t *testing.T) { | ||||
| 
 | ||||
| 	// Define test data
 | ||||
| 	location := "Kilifi" | ||||
| 	 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { | ||||
| 
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| @ -328,7 +324,7 @@ func TestSaveLocation(t *testing.T) { | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Verify that the DATA_LOCATION entry has been updated with the temporary value
 | ||||
| 	storedLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION) | ||||
| 	storedLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) | ||||
| 	assert.Equal(t, location, string(storedLocation)) | ||||
| } | ||||
| 
 | ||||
| @ -336,7 +332,7 @@ func TestSaveOfferings(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -348,7 +344,7 @@ func TestSaveOfferings(t *testing.T) { | ||||
| 	// Define test data
 | ||||
| 	offerings := "Bananas" | ||||
| 
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| @ -367,7 +363,7 @@ func TestSaveOfferings(t *testing.T) { | ||||
| 	assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 	// Verify that the DATA_OFFERINGS entry has been updated with the temporary value
 | ||||
| 	storedOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS) | ||||
| 	storedOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) | ||||
| 	assert.Equal(t, offerings, string(storedOfferings)) | ||||
| } | ||||
| 
 | ||||
| @ -375,7 +371,7 @@ func TestSaveGender(t *testing.T) { | ||||
| 	sessionId := "session123" | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 	 | ||||
| 
 | ||||
| 	fm, _ := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_allow_update, _ := fm.GetFlag("flag_allow_update") | ||||
| @ -413,7 +409,7 @@ func TestSaveGender(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { | ||||
| 			if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 
 | ||||
| @ -433,7 +429,7 @@ func TestSaveGender(t *testing.T) { | ||||
| 			assert.Equal(t, resource.Result{}, res) | ||||
| 
 | ||||
| 			// Verify that the DATA_GENDER entry has been updated with the temporary value
 | ||||
| 			storedGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_GENDER) | ||||
| 			storedGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_GENDER) | ||||
| 			assert.Equal(t, tt.expectedGender, string(storedGender)) | ||||
| 		}) | ||||
| 	} | ||||
| @ -487,7 +483,6 @@ func TestSaveTemporaryPin(t *testing.T) { | ||||
| 			if err != nil { | ||||
| 				t.Error(err) | ||||
| 			} | ||||
| 
 | ||||
| 			// Assert that the Result FlagSet has the required flags after language switch
 | ||||
| 			assert.Equal(t, res, tt.expectedResult, "Result should match expected result") | ||||
| 		}) | ||||
| @ -518,7 +513,7 @@ func TestCheckIdentifier(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) | ||||
| 			err := store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(tt.publicKey)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -562,12 +557,12 @@ func TestGetAmount(t *testing.T) { | ||||
| 	amount := "0.03" | ||||
| 	activeSym := "SRF" | ||||
| 
 | ||||
| 	err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amount)) | ||||
| 	err := store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(amount)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(activeSym)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(activeSym)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @ -593,7 +588,7 @@ func TestGetRecipient(t *testing.T) { | ||||
| 
 | ||||
| 	recepient := "0xcasgatweksalw1018221" | ||||
| 
 | ||||
| 	err := store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recepient)) | ||||
| 	err := store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recepient)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| @ -722,12 +717,12 @@ func TestResetAllowUpdate(t *testing.T) { | ||||
| 
 | ||||
| func TestResetAccountAuthorized(t *testing.T) { | ||||
| 	fm, err := NewFlagManager(flagsPath) | ||||
| 
 | ||||
| 	flag_account_authorized, _ := fm.parser.GetFlag("flag_account_authorized") | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	flag_account_authorized, _ := fm.parser.GetFlag("flag_account_authorized") | ||||
| 
 | ||||
| 	// Define test cases
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| @ -745,7 +740,6 @@ func TestResetAccountAuthorized(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 
 | ||||
| 			// Create the Handlers instance with the mock flag manager
 | ||||
| 			h := &Handlers{ | ||||
| 				flagManager: fm.parser, | ||||
| @ -904,10 +898,7 @@ func TestAuthorize(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			// Create context with session ID
 | ||||
| 			ctx := context.WithValue(context.Background(), "SessionId", sessionId) | ||||
| 
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(accountPIN)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(accountPIN)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1034,7 +1025,7 @@ func TestVerifyCreatePin(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte("1234")) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte("1234")) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1067,18 +1058,14 @@ func TestCheckAccountStatus(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		publicKey      []byte | ||||
| 		serverResponse *api.OKResponse | ||||
| 		response       *models.TrackStatusResult | ||||
| 		expectedResult resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:      "Test when account is on the Sarafu network", | ||||
| 			publicKey: []byte("TrackingId1234"), | ||||
| 			serverResponse: &api.OKResponse{ | ||||
| 				Ok:          true, | ||||
| 				Description: "Account creation succeeded", | ||||
| 				Result: map[string]any{ | ||||
| 					"active": true, | ||||
| 				}, | ||||
| 			response: &models.TrackStatusResult{ | ||||
| 				Active: true, | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagSet:   []uint32{flag_account_success}, | ||||
| @ -1088,12 +1075,8 @@ func TestCheckAccountStatus(t *testing.T) { | ||||
| 		{ | ||||
| 			name:      "Test when the account is not yet on the sarafu network", | ||||
| 			publicKey: []byte("TrackingId1234"), | ||||
| 			serverResponse: &api.OKResponse{ | ||||
| 				Ok:          true, | ||||
| 				Description: "Account creation succeeded", | ||||
| 				Result: map[string]any{ | ||||
| 					"active": false, | ||||
| 				}, | ||||
| 			response: &models.TrackStatusResult{ | ||||
| 				Active: false, | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagSet:   []uint32{flag_account_pending}, | ||||
| @ -1111,12 +1094,12 @@ func TestCheckAccountStatus(t *testing.T) { | ||||
| 				flagManager:    fm.parser, | ||||
| 			} | ||||
| 
 | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(tt.publicKey)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 
 | ||||
| 			mockAccountService.On("TrackAccountStatus", string(tt.publicKey)).Return(tt.serverResponse, nil) | ||||
| 			mockAccountService.On("TrackAccountStatus", string(tt.publicKey)).Return(tt.response, nil) | ||||
| 
 | ||||
| 			// Call the method under test
 | ||||
| 			res, _ := h.CheckAccountStatus(ctx, "check_account_status", []byte("")) | ||||
| @ -1264,15 +1247,15 @@ func TestInitiateTransaction(t *testing.T) { | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(tt.Amount)) | ||||
| 			err := store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(tt.Amount)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(tt.Recipient)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(tt.Recipient)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 			err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.ActiveSym)) | ||||
| 			err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(tt.ActiveSym)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1446,7 +1429,7 @@ func TestValidateAmount(t *testing.T) { | ||||
| 
 | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			err := store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) | ||||
| 			err := store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(tt.activeBal)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1550,11 +1533,11 @@ func TestCheckBalance(t *testing.T) { | ||||
| 				accountService: mockAccountService, | ||||
| 			} | ||||
| 
 | ||||
| 			err := store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.activeSym)) | ||||
| 			err := store.WriteEntry(ctx, tt.sessionId, common.DATA_ACTIVE_SYM, []byte(tt.activeSym)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 			err = store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) | ||||
| 			err = store.WriteEntry(ctx, tt.sessionId, common.DATA_ACTIVE_BAL, []byte(tt.activeBal)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1589,13 +1572,13 @@ func TestGetProfile(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name         string | ||||
| 		languageCode string | ||||
| 		keys         []utils.DataTyp | ||||
| 		keys         []common.DataTyp | ||||
| 		profileInfo  []string | ||||
| 		result       resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:         "Test with full profile information in eng", | ||||
| 			keys:         []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, | ||||
| 			keys:         []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, | ||||
| 			profileInfo:  []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, | ||||
| 			languageCode: "eng", | ||||
| 			result: resource.Result{ | ||||
| @ -1607,7 +1590,7 @@ func TestGetProfile(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:         "Test with with profile information in swa", | ||||
| 			keys:         []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, | ||||
| 			keys:         []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, | ||||
| 			profileInfo:  []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, | ||||
| 			languageCode: "swa", | ||||
| 			result: resource.Result{ | ||||
| @ -1619,7 +1602,7 @@ func TestGetProfile(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:         "Test with with profile information with language that is not yet supported", | ||||
| 			keys:         []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, | ||||
| 			keys:         []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, | ||||
| 			profileInfo:  []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, | ||||
| 			languageCode: "nor", | ||||
| 			result: resource.Result{ | ||||
| @ -1728,7 +1711,7 @@ func TestConfirmPin(t *testing.T) { | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			// Set up the expected behavior of the mock
 | ||||
| 			err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.temporarypin)) | ||||
| 			err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.temporarypin)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| @ -1757,43 +1740,21 @@ func TestFetchCustodialBalances(t *testing.T) { | ||||
| 	ctx, store := InitializeTestStore(t) | ||||
| 	ctx = context.WithValue(ctx, "SessionId", sessionId) | ||||
| 
 | ||||
| 	err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 	err = store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		balanceResonse *models.BalanceResponse | ||||
| 		expectedResult resource.Result | ||||
| 		name            string | ||||
| 		balanceResponse *models.BalanceResult | ||||
| 		expectedResult  resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "Test when fetch custodial balances is not a success", | ||||
| 			balanceResonse: &models.BalanceResponse{ | ||||
| 				Ok: false, | ||||
| 				Result: struct { | ||||
| 					Balance string      `json:"balance"` | ||||
| 					Nonce   json.Number `json:"nonce"` | ||||
| 				}{ | ||||
| 					Balance: "0.003 CELO", | ||||
| 					Nonce:   json.Number("0"), | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagSet: []uint32{flag_api_error}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Test when fetch custodial balances is a success", | ||||
| 			balanceResonse: &models.BalanceResponse{ | ||||
| 				Ok: true, | ||||
| 				Result: struct { | ||||
| 					Balance string      `json:"balance"` | ||||
| 					Nonce   json.Number `json:"nonce"` | ||||
| 				}{ | ||||
| 					Balance: "0.003 CELO", | ||||
| 					Nonce:   json.Number("0"), | ||||
| 				}, | ||||
| 			balanceResponse: &models.BalanceResult{ | ||||
| 				Balance: "0.003 CELO", | ||||
| 				Nonce:   json.Number("0"), | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagReset: []uint32{flag_api_error}, | ||||
| @ -1813,7 +1774,7 @@ func TestFetchCustodialBalances(t *testing.T) { | ||||
| 			} | ||||
| 
 | ||||
| 			// Set up the expected behavior of the mock
 | ||||
| 			mockAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) | ||||
| 			mockAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResponse, nil) | ||||
| 
 | ||||
| 			// Call the method
 | ||||
| 			res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) | ||||
| @ -1833,28 +1794,33 @@ func TestSetDefaultVoucher(t *testing.T) { | ||||
| 	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()) | ||||
| 	} | ||||
| 
 | ||||
| 	publicKey := "0X13242618721" | ||||
| 
 | ||||
| 	tests := []struct { | ||||
| 		name           string | ||||
| 		vouchersResp   *models.VoucherHoldingResponse | ||||
| 		vouchersResp   []dataserviceapi.TokenHoldings | ||||
| 		expectedResult resource.Result | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:         "Test no vouchers available", | ||||
| 			vouchersResp: []dataserviceapi.TokenHoldings{}, | ||||
| 			expectedResult: resource.Result{ | ||||
| 				FlagSet: []uint32{flag_no_active_voucher}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "Test set default voucher when no active voucher is set", | ||||
| 			vouchersResp: &models.VoucherHoldingResponse{ | ||||
| 				Ok:          true, | ||||
| 				Description: "Vouchers fetched successfully", | ||||
| 				Result: models.VoucherResult{ | ||||
| 					Holdings: []dataserviceapi.TokenHoldings{ | ||||
| 						{ | ||||
| 							ContractAddress: "0x123", | ||||
| 							TokenSymbol:     "TOKEN1", | ||||
| 							TokenDecimals:   "18", | ||||
| 							Balance:         "100", | ||||
| 						}, | ||||
| 					}, | ||||
| 			vouchersResp: []dataserviceapi.TokenHoldings{ | ||||
| 				dataserviceapi.TokenHoldings{ | ||||
| 					ContractAddress: "0x123", | ||||
| 					TokenSymbol:     "TOKEN1", | ||||
| 					TokenDecimals:   "18", | ||||
| 					Balance:         "100", | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectedResult: resource.Result{}, | ||||
| @ -1871,14 +1837,14 @@ func TestSetDefaultVoucher(t *testing.T) { | ||||
| 				flagManager:    fm.parser, | ||||
| 			} | ||||
| 
 | ||||
| 			err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 			err := store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 			if err != nil { | ||||
| 				t.Fatal(err) | ||||
| 			} | ||||
| 
 | ||||
| 			mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) | ||||
| 
 | ||||
| 			res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("")) | ||||
| 			res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input")) | ||||
| 
 | ||||
| 			assert.NoError(t, err) | ||||
| 
 | ||||
| @ -1904,13 +1870,12 @@ func TestCheckVouchers(t *testing.T) { | ||||
| 		prefixDb:       spdb, | ||||
| 	} | ||||
| 
 | ||||
| 	err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 	err := store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(publicKey)) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
| 	mockVouchersResponse := &models.VoucherHoldingResponse{} | ||||
| 	mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ | ||||
| 	mockVouchersResponse := []dataserviceapi.TokenHoldings{ | ||||
| 		{ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, | ||||
| 		{ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, | ||||
| 	} | ||||
| @ -2018,7 +1983,7 @@ func TestSetVoucher(t *testing.T) { | ||||
| 	expectedData := fmt.Sprintf("%s,%s,%s,%s", tempData.TokenSymbol, tempData.Balance, tempData.TokenDecimals, tempData.ContractAddress) | ||||
| 
 | ||||
| 	// store the expectedData
 | ||||
| 	if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)); err != nil { | ||||
| 	if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(expectedData)); err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -1,10 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| type AccountResponse struct { | ||||
| 	Ok          bool   `json:"ok"` | ||||
| 	Description string `json:"description"` // Include the description field
 | ||||
| 	Result      struct { | ||||
| 		PublicKey  string `json:"publicKey"` | ||||
| 		TrackingId string `json:"trackingId"` | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,12 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import "encoding/json" | ||||
| 
 | ||||
| 
 | ||||
| type BalanceResponse struct { | ||||
| 	Ok     bool `json:"ok"` | ||||
| 	Result struct { | ||||
| 		Balance string      `json:"balance"` | ||||
| 		Nonce   json.Number `json:"nonce"` | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| ) | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| type TrackStatusResponse struct { | ||||
| 	Ok     bool `json:"ok"` | ||||
| 	Result struct { | ||||
| 		Transaction struct { | ||||
| 			CreatedAt     time.Time   `json:"createdAt"` | ||||
| 			Status        string      `json:"status"` | ||||
| 			TransferValue json.Number `json:"transferValue"` | ||||
| 			TxHash        string      `json:"txHash"` | ||||
| 			TxType        string      `json:"txType"` | ||||
| 		} | ||||
| 	} `json:"result"` | ||||
| } | ||||
| @ -1,14 +0,0 @@ | ||||
| package models | ||||
| 
 | ||||
| import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 
 | ||||
| type VoucherHoldingResponse struct { | ||||
| 	Ok          bool          `json:"ok"` | ||||
| 	Description string        `json:"description"` | ||||
| 	Result      VoucherResult `json:"result"` | ||||
| } | ||||
| 
 | ||||
| // VoucherResult holds the list of token holdings
 | ||||
| type VoucherResult struct { | ||||
| 	Holdings []dataserviceapi.TokenHoldings `json:"holdings"` | ||||
| } | ||||
| @ -11,11 +11,11 @@ import ( | ||||
| 	"git.defalsify.org/vise.git/logging" | ||||
| 	"git.defalsify.org/vise.git/resource" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/storage" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/testservice" | ||||
| 	"git.grassecon.net/urdt/ussd/internal/testutil/testtag" | ||||
| 	testdataloader "github.com/peteole/testdata-loader" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| @ -73,7 +73,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 		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.SetPersister(pe) | ||||
| 
 | ||||
| @ -83,7 +83,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 	} | ||||
| 
 | ||||
| 	if testtag.AccountService == nil { | ||||
| 		testtag.AccountService = &server.AccountService{} | ||||
| 		testtag.AccountService = &remote.AccountService{} | ||||
| 	} | ||||
| 
 | ||||
| 	switch testtag.AccountService.(type) { | ||||
| @ -91,7 +91,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { | ||||
| 		go func() { | ||||
| 			eventChannel <- false | ||||
| 		}() | ||||
| 	case *server.AccountService: | ||||
| 	case *remote.AccountService: | ||||
| 		go func() { | ||||
| 			time.Sleep(5 * time.Second) // Wait for 5 seconds
 | ||||
| 			eventChannel <- true | ||||
|  | ||||
| @ -3,8 +3,8 @@ package mocks | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 	"github.com/stretchr/testify/mock" | ||||
| ) | ||||
| 
 | ||||
| @ -13,28 +13,33 @@ type MockAccountService struct { | ||||
| 	mock.Mock | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| func (m *MockAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	args := m.Called() | ||||
| 	return args.Get(0).(*api.OKResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.AccountResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*models.BalanceResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.BalanceResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { | ||||
| 	args := m.Called(trackingId) | ||||
| 	return args.Get(0).(*models.TrackStatusResponse), args.Error(1) | ||||
| 	return args.Get(0).(*models.TrackStatusResult), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) { | ||||
| 
 | ||||
| func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*api.OKResponse), args.Error(1) | ||||
| 	return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	args := m.Called(publicKey) | ||||
| 	return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) | ||||
| 	return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) | ||||
| } | ||||
| 
 | ||||
| func(m MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	args := m.Called(address) | ||||
| 	return args.Get(0).(*models.VoucherDataResult), args.Error(1) | ||||
| } | ||||
|  | ||||
| @ -3,88 +3,50 @@ package testservice | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"git.grassecon.net/urdt/ussd/internal/models" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| ) | ||||
| 
 | ||||
| type TestAccountService struct { | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { | ||||
| 	return &api.OKResponse{ | ||||
| 		Ok:          true, | ||||
| 		Description: "Account creation succeeded", | ||||
| 		Result: map[string]any{ | ||||
| 			"trackingId": "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", | ||||
| 			"publicKey":  "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", | ||||
| 		}, | ||||
| func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	return &models.AccountResult { | ||||
| 		TrackingId: "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", | ||||
| 		PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { | ||||
| 	balanceResponse := &models.BalanceResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Balance string      `json:"balance"` | ||||
| 			Nonce   json.Number `json:"nonce"` | ||||
| 		}{ | ||||
| 			Balance: "0.003 CELO", | ||||
| 			Nonce:   json.Number("0"), | ||||
| 		}, | ||||
| func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	balanceResponse := &models.BalanceResult { | ||||
| 		Balance: "0.003 CELO", | ||||
| 		Nonce:   json.Number("0"), | ||||
| 	} | ||||
| 
 | ||||
| 	return balanceResponse, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { | ||||
| 	trackResponse := &models.TrackStatusResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: struct { | ||||
| 			Transaction struct { | ||||
| 				CreatedAt     time.Time   "json:\"createdAt\"" | ||||
| 				Status        string      "json:\"status\"" | ||||
| 				TransferValue json.Number "json:\"transferValue\"" | ||||
| 				TxHash        string      "json:\"txHash\"" | ||||
| 				TxType        string      "json:\"txType\"" | ||||
| 			} | ||||
| 		}{ | ||||
| 			Transaction: models.Transaction{ | ||||
| 				CreatedAt:     time.Now(), | ||||
| 				Status:        "SUCCESS", | ||||
| 				TransferValue: json.Number("0.5"), | ||||
| 				TxHash:        "0x123abc456def", | ||||
| 				TxType:        "transfer", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	return trackResponse, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { | ||||
| 	return &api.OKResponse{ | ||||
| 		Ok:          true, | ||||
| 		Description: "Account creation succeeded", | ||||
| 		Result: map[string]any{ | ||||
| 			"active": true, | ||||
| 		}, | ||||
| func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { | ||||
| 	return &models.TrackStatusResult { | ||||
| 		Active: true, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { | ||||
| 	return &models.VoucherHoldingResponse{ | ||||
| 		Ok: true, | ||||
| 		Result: models.VoucherResult{ | ||||
| 			Holdings: []dataserviceapi.TokenHoldings{ | ||||
| 				{ | ||||
| 					ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", | ||||
| 					TokenSymbol:     "SRF", | ||||
| 					TokenDecimals:   "6", | ||||
| 					Balance:         "2745987", | ||||
| 				}, | ||||
| 			}, | ||||
| func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	return []dataserviceapi.TokenHoldings { | ||||
| 		dataserviceapi.TokenHoldings { | ||||
| 			ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", | ||||
| 			TokenSymbol:     "SRF", | ||||
| 			TokenDecimals:   "6", | ||||
| 			Balance:         "2745987", | ||||
| 		}, | ||||
| 	}, nil | ||||
| 	}, nil  | ||||
| } | ||||
| 
 | ||||
| func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	return []dataserviceapi.Last10TxResponse{}, nil | ||||
| } | ||||
| 
 | ||||
| func(m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	return &models.VoucherDataResult{}, nil | ||||
| } | ||||
|  | ||||
| @ -3,10 +3,10 @@ | ||||
| package testtag | ||||
| 
 | ||||
| import ( | ||||
| 	"git.grassecon.net/urdt/ussd/internal/handlers/server" | ||||
| 	"git.grassecon.net/urdt/ussd/remote" | ||||
| 	accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	AccountService server.AccountServiceInterface = &accountservice.TestAccountService{} | ||||
| 	AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{} | ||||
| ) | ||||
|  | ||||
							
								
								
									
										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 | ||||
| } | ||||
| @ -13,7 +13,7 @@ | ||||
|                 }, | ||||
|                 { | ||||
|                     "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", | ||||
|  | ||||
							
								
								
									
										6
									
								
								models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| package models | ||||
| 
 | ||||
| type AccountResult struct { | ||||
| 	PublicKey string `json:"publicKey"` | ||||
| 	TrackingId string `json:"trackingId"` | ||||
| } | ||||
							
								
								
									
										9
									
								
								models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| package models | ||||
| 
 | ||||
| import "encoding/json" | ||||
| 
 | ||||
| 
 | ||||
| type BalanceResult struct { | ||||
| 	Balance string      `json:"balance"` | ||||
| 	Nonce   json.Number `json:"nonce"` | ||||
| } | ||||
							
								
								
									
										18
									
								
								models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package models | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type Transaction struct { | ||||
| 	CreatedAt     time.Time   `json:"createdAt"` | ||||
| 	Status        string      `json:"status"` | ||||
| 	TransferValue json.Number `json:"transferValue"` | ||||
| 	TxHash        string      `json:"txHash"` | ||||
| 	TxType        string      `json:"txType"` | ||||
| } | ||||
| 
 | ||||
| type TrackStatusResult struct { | ||||
| 	Active	bool `json:"active"` | ||||
| } | ||||
							
								
								
									
										21
									
								
								models/vouchersresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								models/vouchersresponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| package models | ||||
| 
 | ||||
| import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 
 | ||||
| //type VoucherHoldingResponse struct {
 | ||||
| //	Ok          bool          `json:"ok"`
 | ||||
| //	Description string        `json:"description"`
 | ||||
| //	Result      VoucherResult `json:"result"`
 | ||||
| //}
 | ||||
| 
 | ||||
| // VoucherResult holds the list of token holdings
 | ||||
| type VoucherResult struct { | ||||
| 	Holdings []dataserviceapi.TokenHoldings `json:"holdings"` | ||||
| } | ||||
| 
 | ||||
| type VoucherDataResult struct { | ||||
| 	TokenName	string `json:"tokenName"` | ||||
| 	TokenSymbol     string `json:"tokenSymbol"` | ||||
| 	TokenDecimals   string `json:"tokenDecimals"` | ||||
| 	SinkAddress	string `json:"sinkAddress"` | ||||
| } | ||||
							
								
								
									
										224
									
								
								remote/accountservice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								remote/accountservice.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,224 @@ | ||||
| package remote | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 
 | ||||
| 	dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" | ||||
| 	"github.com/grassrootseconomics/eth-custodial/pkg/api" | ||||
| 	"git.grassecon.net/urdt/ussd/config" | ||||
| 	"git.grassecon.net/urdt/ussd/models" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| ) | ||||
| 
 | ||||
| type AccountServiceInterface interface { | ||||
| 	CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) | ||||
| 	CreateAccount(ctx context.Context) (*models.AccountResult, error) | ||||
| 	TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) | ||||
| 	FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) | ||||
| 	FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) | ||||
| 	VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) | ||||
| } | ||||
| 
 | ||||
| type AccountService struct { | ||||
| } | ||||
| 
 | ||||
| // Parameters:
 | ||||
| //   - trackingId: A unique identifier for the account.This should be obtained from a previous call to
 | ||||
| //     CreateAccount or a similar function that returns an AccountResponse. The `trackingId` field in the
 | ||||
| //     AccountResponse struct can be used here to check the account status during a transaction.
 | ||||
| //
 | ||||
| // Returns:
 | ||||
| //   - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil
 | ||||
| func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { | ||||
| 	var r models.TrackStatusResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.TrackURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doCustodialRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account whose balance needs to be checked.
 | ||||
| func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { | ||||
| 	var balanceResult models.BalanceResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.BalanceURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doCustodialRequest(ctx, req, &balanceResult) | ||||
| 	return &balanceResult, err | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // CreateAccount creates a new account in the custodial system.
 | ||||
| // Returns:
 | ||||
| //   - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account.
 | ||||
| //     If there is an error during the request or processing, this will be nil.
 | ||||
| //   - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data.
 | ||||
| //     If no error occurs, this will be nil.
 | ||||
| func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { | ||||
| 	var r models.AccountResult | ||||
| 	// Create a new request
 | ||||
| 	req, err := http.NewRequest("POST", config.CreateAccountURL, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doCustodialRequest(ctx, req, &r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &r, nil | ||||
| } | ||||
| 
 | ||||
| // FetchVouchers retrieves the token holdings for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { | ||||
| 	var r []dataserviceapi.TokenHoldings | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doDataRequest(ctx, req, r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint
 | ||||
| // Parameters:
 | ||||
| //   - publicKey: The public key associated with the account.
 | ||||
| func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { | ||||
| 	var r []dataserviceapi.Last10TxResponse | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err =  doDataRequest(ctx, req, r) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // VoucherData retrieves voucher metadata from the data indexer API endpoint.
 | ||||
| // Parameters:
 | ||||
| //   - address: The voucher address.
 | ||||
| func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { | ||||
| 	var voucherDataResult models.VoucherDataResult | ||||
| 
 | ||||
| 	ep, err := url.JoinPath(config.VoucherDataURL, address) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	req, err := http.NewRequest("GET", ep, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	_, err = doCustodialRequest(ctx, req, &voucherDataResult) | ||||
| 	return &voucherDataResult, err | ||||
| } | ||||
| 
 | ||||
| func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	var okResponse  api.OKResponse | ||||
| 	var errResponse api.ErrResponse | ||||
| 
 | ||||
| 	req.Header.Set("Content-Type", "application/json") | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		errResponse.Description = err.Error() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	body, err := io.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if resp.StatusCode >= http.StatusBadRequest { | ||||
| 		err := json.Unmarshal([]byte(body), &errResponse) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, errors.New(errResponse.Description) | ||||
| 	} | ||||
| 	err = json.Unmarshal([]byte(body), &okResponse) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(okResponse.Result) == 0 { | ||||
| 		return nil, errors.New("Empty api result") | ||||
| 	} | ||||
| 	return &okResponse, nil | ||||
| 
 | ||||
| 	v, err := json.Marshal(okResponse.Result) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	err = json.Unmarshal(v, &rcpt) | ||||
| 	return &okResponse, err | ||||
| } | ||||
| 
 | ||||
| func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	req.Header.Set("X-GE-KEY", config.CustodialAPIKey) | ||||
| 	return doRequest(ctx, req, rcpt) | ||||
| } | ||||
| 
 | ||||
| func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { | ||||
| 	req.Header.Set("X-GE-KEY", config.DataAPIKey) | ||||
| 	return doRequest(ctx, req, rcpt) | ||||
| } | ||||
							
								
								
									
										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 | ||||
| INCMP _ 0 | ||||
| INCMP * pin_reset_success | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										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 +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 reset_pin 2 | ||||
| MOUT guard_pin 3 | ||||
| MOUT back 0 | ||||
| HALT | ||||
| INCMP _ 0 | ||||
| INCMP my_account 0 | ||||
| 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 | ||||
| INCMP main 0 | ||||
| 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_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_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
									
								
								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 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user