forked from grassrootseconomics/visedriver
		
	Compare commits
	
		
			29 Commits
		
	
	
		
			master
			...
			menu-optio
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| bb28112f47 | |||
| 5b5352f569 | |||
| e3e8bfe85c | |||
| 10c917b6da | |||
| 7c7150b103 | |||
| 99fcb9706e | |||
| 67062a41ad | |||
| 123fdec009 | |||
| 20694d956b | |||
| 10abad9e59 | |||
| ca366ee2bc | |||
| c7f0ddec9b | |||
| 2fe4ada5d3 | |||
| b0342936e1 | |||
| 63d060afe2 | |||
| 92d212f891 | |||
| b25288db2c | |||
| 5aed7c647f | |||
| 8765077177 | |||
| 4a6e4ebe55 | |||
| 525eee93d4 | |||
| 0c3ef357df | |||
| c2d2bd250a | |||
| cb2254664d | |||
| f010d097ed | |||
| c5fcb79e9e | |||
| cb77e44cbd | |||
| 4d7c584394 | |||
| 5ff06e8626 | 
							
								
								
									
										20
									
								
								.env.example
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								.env.example
									
									
									
									
									
								
							@ -1,20 +0,0 @@
 | 
			
		||||
#Serve Http
 | 
			
		||||
PORT=7123
 | 
			
		||||
HOST=127.0.0.1
 | 
			
		||||
 | 
			
		||||
#AfricasTalking USSD POST endpoint
 | 
			
		||||
AT_ENDPOINT=/ussd/africastalking
 | 
			
		||||
 | 
			
		||||
#PostgreSQL
 | 
			
		||||
DB_CONN=postgres://postgres:strongpass@localhost:5432/urdt_ussd
 | 
			
		||||
#DB_TIMEZONE=Africa/Nairobi
 | 
			
		||||
#DB_SCHEMA=vise
 | 
			
		||||
 | 
			
		||||
#External API Calls
 | 
			
		||||
CUSTODIAL_URL_BASE=http://localhost:5003
 | 
			
		||||
BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr
 | 
			
		||||
DATA_URL_BASE=http://localhost:5006
 | 
			
		||||
 | 
			
		||||
#Language
 | 
			
		||||
DEFAULT_LANGUAGE=eng
 | 
			
		||||
LANGUAGES=eng, swa
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -4,6 +4,3 @@ go.work*
 | 
			
		||||
**/*/*.bin
 | 
			
		||||
**/*/.state/
 | 
			
		||||
cmd/.state/
 | 
			
		||||
id_*
 | 
			
		||||
*.gdbm
 | 
			
		||||
*.log
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										9
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
# ussd
 | 
			
		||||
 | 
			
		||||
> USSD
 | 
			
		||||
 | 
			
		||||
USSD service.
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
[AGPL-3.0](LICENSE).
 | 
			
		||||
							
								
								
									
										215
									
								
								cmd/http/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								cmd/http/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,215 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/asm"
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
	fsdb "git.defalsify.org/vise.git/db/fs"
 | 
			
		||||
	gdbmdb "git.defalsify.org/vise.git/db/gdbm"
 | 
			
		||||
	"git.defalsify.org/vise.git/engine"
 | 
			
		||||
	"git.defalsify.org/vise.git/resource"
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
 | 
			
		||||
	httpserver "git.grassecon.net/urdt/ussd/internal/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg = logging.NewVanilla()
 | 
			
		||||
	scriptDir = path.Join("services", "registration")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getFlags(fp string, debug bool) (*asm.FlagParser, error) {
 | 
			
		||||
	flagParser := asm.NewFlagParser().WithDebug()
 | 
			
		||||
	_, err := flagParser.Load(fp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return flagParser, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, userdataStore db.Db) (*ussd.Handlers, error) {
 | 
			
		||||
 | 
			
		||||
	ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rs.AddLocalFunc("select_language", ussdHandlers.SetLanguage)
 | 
			
		||||
	rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
 | 
			
		||||
	rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
 | 
			
		||||
	rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
 | 
			
		||||
	rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
 | 
			
		||||
	rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
 | 
			
		||||
	rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
 | 
			
		||||
	rs.AddLocalFunc("quit", ussdHandlers.Quit)
 | 
			
		||||
	rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
 | 
			
		||||
	rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
 | 
			
		||||
	rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
 | 
			
		||||
	rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
 | 
			
		||||
	rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
 | 
			
		||||
	rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
 | 
			
		||||
	rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
 | 
			
		||||
	rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
 | 
			
		||||
	rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
 | 
			
		||||
	rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
 | 
			
		||||
	rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
 | 
			
		||||
	rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
 | 
			
		||||
	rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
 | 
			
		||||
	rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
 | 
			
		||||
	rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
 | 
			
		||||
	rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
 | 
			
		||||
	rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
 | 
			
		||||
	rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
 | 
			
		||||
	rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
 | 
			
		||||
	rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
 | 
			
		||||
	rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
 | 
			
		||||
	rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
 | 
			
		||||
	rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
 | 
			
		||||
	rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
 | 
			
		||||
 | 
			
		||||
	return ussdHandlers, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ensureDbDir(dbDir string) error {
 | 
			
		||||
	err := os.MkdirAll(dbDir, 0700)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("state dir create exited with error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getStateStore(dbDir string, ctx context.Context) (db.Db, error) {
 | 
			
		||||
	store := gdbmdb.NewGdbmDb()
 | 
			
		||||
	storeFile := path.Join(dbDir, "state.gdbm")
 | 
			
		||||
	store.Connect(ctx, storeFile)
 | 
			
		||||
	return store, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
 | 
			
		||||
	store := gdbmdb.NewGdbmDb()
 | 
			
		||||
	storeFile := path.Join(dbDir, "userdata.gdbm")
 | 
			
		||||
	store.Connect(ctx, storeFile)
 | 
			
		||||
	
 | 
			
		||||
	return store
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
 | 
			
		||||
	store := fsdb.NewFsDb()
 | 
			
		||||
	err := store.Connect(ctx, resourceDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rfs := resource.NewDbResource(store)
 | 
			
		||||
	return rfs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var dbDir string
 | 
			
		||||
	var resourceDir string
 | 
			
		||||
	var size uint
 | 
			
		||||
	var engineDebug bool
 | 
			
		||||
	var stateDebug bool
 | 
			
		||||
	var host string
 | 
			
		||||
	var port uint
 | 
			
		||||
	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
 | 
			
		||||
	flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
 | 
			
		||||
	flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
 | 
			
		||||
	flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
 | 
			
		||||
	flag.UintVar(&size, "s", 160, "max size of output")
 | 
			
		||||
	flag.StringVar(&host, "h", "127.0.0.1", "http host")
 | 
			
		||||
	flag.UintVar(&port, "p", 7123, "http port")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir,  "outputsize", size)
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	pfp := path.Join(scriptDir, "pp.csv")
 | 
			
		||||
	flagParser, err := getFlags(pfp, true)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg := engine.Config{
 | 
			
		||||
		Root:       "root",
 | 
			
		||||
		OutputSize: uint32(size),
 | 
			
		||||
		FlagCount:  uint32(16),
 | 
			
		||||
	}
 | 
			
		||||
	if stateDebug {
 | 
			
		||||
		cfg.StateDebug = true
 | 
			
		||||
	}
 | 
			
		||||
	if engineDebug {
 | 
			
		||||
		cfg.EngineDebug = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rs, err := getResource(resourceDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = ensureDbDir(dbDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userdataStore := getUserdataDb(dbDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	defer userdataStore.Close()
 | 
			
		||||
 | 
			
		||||
	dbResource, ok := rs.(*resource.DbResource)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hl, err := getHandler(flagParser, dbResource, userdataStore)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	stateStore, err := getStateStore(dbDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
	defer stateStore.Close()
 | 
			
		||||
 | 
			
		||||
	rp := &httpserver.DefaultRequestParser{}
 | 
			
		||||
	//sh := httpserver.NewSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl.Init)
 | 
			
		||||
	sh := httpserver.NewSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
 | 
			
		||||
	s := &http.Server{
 | 
			
		||||
		Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
 | 
			
		||||
		Handler: sh,
 | 
			
		||||
	}
 | 
			
		||||
	s.RegisterOnShutdown(sh.Shutdown)
 | 
			
		||||
 | 
			
		||||
	cint := make(chan os.Signal)
 | 
			
		||||
	cterm := make(chan os.Signal)
 | 
			
		||||
	signal.Notify(cint, os.Interrupt, syscall.SIGINT)
 | 
			
		||||
	signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
 | 
			
		||||
	go func() {
 | 
			
		||||
		select {
 | 
			
		||||
		case _ = <-cint:
 | 
			
		||||
		case _ = <-cterm:
 | 
			
		||||
		}
 | 
			
		||||
		s.Shutdown(ctx)
 | 
			
		||||
	}()
 | 
			
		||||
	err = s.ListenAndServe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.Infof("Server closed with error", "err", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										201
									
								
								cmd/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								cmd/main.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,201 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/asm"
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
	fsdb "git.defalsify.org/vise.git/db/fs"
 | 
			
		||||
	gdbmdb "git.defalsify.org/vise.git/db/gdbm"
 | 
			
		||||
	"git.defalsify.org/vise.git/engine"
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
	"git.defalsify.org/vise.git/persist"
 | 
			
		||||
	"git.defalsify.org/vise.git/resource"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg      = logging.NewVanilla()
 | 
			
		||||
	scriptDir = path.Join("services", "registration")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getParser(fp string, debug bool) (*asm.FlagParser, error) {
 | 
			
		||||
	flagParser := asm.NewFlagParser().WithDebug()
 | 
			
		||||
	_, err := flagParser.Load(fp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return flagParser, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, pe *persist.Persister, userdataStore db.Db) (*ussd.Handlers, error) {
 | 
			
		||||
 | 
			
		||||
	ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	ussdHandlers = ussdHandlers.WithPersister(pe)
 | 
			
		||||
	rs.AddLocalFunc("select_language", ussdHandlers.SetLanguage)
 | 
			
		||||
	rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
 | 
			
		||||
	rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
 | 
			
		||||
	rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
 | 
			
		||||
	rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
 | 
			
		||||
	rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
 | 
			
		||||
	rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
 | 
			
		||||
	rs.AddLocalFunc("quit", ussdHandlers.Quit)
 | 
			
		||||
	rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
 | 
			
		||||
	rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
 | 
			
		||||
	rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
 | 
			
		||||
	rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
 | 
			
		||||
	rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
 | 
			
		||||
	rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
 | 
			
		||||
	rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
 | 
			
		||||
	rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
 | 
			
		||||
	rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
 | 
			
		||||
	rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
 | 
			
		||||
	rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
 | 
			
		||||
	rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
 | 
			
		||||
	rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
 | 
			
		||||
	rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
 | 
			
		||||
	rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
 | 
			
		||||
	rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
 | 
			
		||||
	rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
 | 
			
		||||
	rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
 | 
			
		||||
	rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
 | 
			
		||||
	rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
 | 
			
		||||
	rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
 | 
			
		||||
	rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
 | 
			
		||||
	rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
 | 
			
		||||
	rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
 | 
			
		||||
	rs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
 | 
			
		||||
	rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
 | 
			
		||||
	rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
 | 
			
		||||
 | 
			
		||||
	return ussdHandlers, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ensureDbDir(dbDir string) error {
 | 
			
		||||
	err := os.MkdirAll(dbDir, 0700)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("state dir create exited with error: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPersister(dbDir string, ctx context.Context) (*persist.Persister, error) {
 | 
			
		||||
	err := ensureDbDir(dbDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	store := gdbmdb.NewGdbmDb()
 | 
			
		||||
	storeFile := path.Join(dbDir, "state.gdbm")
 | 
			
		||||
	store.Connect(ctx, storeFile)
 | 
			
		||||
	pr := persist.NewPersister(store)
 | 
			
		||||
	return pr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
 | 
			
		||||
	store := gdbmdb.NewGdbmDb()
 | 
			
		||||
	storeFile := path.Join(dbDir, "userdata.gdbm")
 | 
			
		||||
	store.Connect(ctx, storeFile)
 | 
			
		||||
 | 
			
		||||
	return store
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
 | 
			
		||||
	store := fsdb.NewFsDb()
 | 
			
		||||
	err := store.Connect(ctx, resourceDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rfs := resource.NewDbResource(store)
 | 
			
		||||
	return rfs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) *engine.DefaultEngine {
 | 
			
		||||
	en := engine.NewEngine(cfg, rs)
 | 
			
		||||
	en = en.WithPersister(pr)
 | 
			
		||||
	return en
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	var dbDir string
 | 
			
		||||
	var size uint
 | 
			
		||||
	var sessionId string
 | 
			
		||||
	var debug bool
 | 
			
		||||
	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
 | 
			
		||||
	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
 | 
			
		||||
	flag.BoolVar(&debug, "d", false, "use engine debug output")
 | 
			
		||||
	flag.UintVar(&size, "s", 160, "max size of output")
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	logg.Infof("start command", "dbdir", dbDir, "outputsize", size)
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	ctx = context.WithValue(ctx, "SessionId", sessionId)
 | 
			
		||||
	pfp := path.Join(scriptDir, "pp.csv")
 | 
			
		||||
	flagParser, err := getParser(pfp, true)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg := engine.Config{
 | 
			
		||||
		Root:       "root",
 | 
			
		||||
		SessionId:  sessionId,
 | 
			
		||||
		OutputSize: uint32(size),
 | 
			
		||||
		FlagCount:  uint32(16),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rs, err := getResource(scriptDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pe, err := getPersister(dbDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	store := getUserdataDb(dbDir, ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbResource, ok := rs.(*resource.DbResource)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hl, err := getHandler(flagParser, dbResource, pe, store)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, err.Error())
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	en := getEngine(cfg, rs, pe)
 | 
			
		||||
	en = en.WithFirst(hl.Init)
 | 
			
		||||
	if debug {
 | 
			
		||||
		en = en.WithDebug(nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = en.Init(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "engine init exited with error: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = engine.Loop(ctx, en, os.Stdin, os.Stdout)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "loop exited with error: %v\n", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,59 +1,10 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/env"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
    CreateAccountURL = "https://custodial.sarafu.africa/api/account/create"
 | 
			
		||||
    TrackStatusURL   = "https://custodial.sarafu.africa/api/track/"
 | 
			
		||||
    BalanceURL       = "https://custodial.sarafu.africa/api/account/status/"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	defaultLanguage		   = "eng"
 | 
			
		||||
	languages []string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	DbConn		string
 | 
			
		||||
	DefaultLanguage	    string
 | 
			
		||||
	Languages	[]string
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func setLanguage() error {
 | 
			
		||||
	defaultLanguage = env.GetEnv("DEFAULT_LANGUAGE", defaultLanguage)
 | 
			
		||||
	languages = strings.Split(env.GetEnv("LANGUAGES", defaultLanguage), ",")
 | 
			
		||||
	haveDefaultLanguage := false
 | 
			
		||||
	for i, v := range(languages) {
 | 
			
		||||
		languages[i] = strings.ReplaceAll(v, " ", "")
 | 
			
		||||
		if languages[i] == defaultLanguage {
 | 
			
		||||
			haveDefaultLanguage = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !haveDefaultLanguage {
 | 
			
		||||
		languages = append([]string{defaultLanguage}, languages...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func setConn() error {
 | 
			
		||||
	DbConn = env.GetEnv("DB_CONN", "")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadConfig initializes the configuration values after environment variables are loaded.
 | 
			
		||||
func LoadConfig() error {
 | 
			
		||||
	err := setConn()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = setLanguage()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	DefaultLanguage = defaultLanguage
 | 
			
		||||
	Languages = languages
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								doc/data.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								doc/data.md
									
									
									
									
									
								
							@ -1,28 +0,0 @@
 | 
			
		||||
# Internals
 | 
			
		||||
 | 
			
		||||
## Version
 | 
			
		||||
 | 
			
		||||
This document describes component versions:
 | 
			
		||||
 | 
			
		||||
* `urdt-ussd` `v0.5.0-beta`
 | 
			
		||||
* `go-vise` `v0.2.2`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## User profile data
 | 
			
		||||
 | 
			
		||||
All user profile items are stored under keys matching the user's session id, prefixed with the 8-bit value `git.defalsify.org/vise.git/db.DATATYPE_USERDATA` (32), and followed with a 16-big big-endian value subprefix.
 | 
			
		||||
 | 
			
		||||
For example, given the sessionId `+254123` and the key `git.grassecon.net/urdt-ussd/common.DATA_PUBLIC_KEY` (2) will be stored under the key:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
0x322b3235343132330002
 | 
			
		||||
 | 
			
		||||
prefix   sessionid       subprefix
 | 
			
		||||
32       2b323534313233  0002
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Sub-prefixes
 | 
			
		||||
 | 
			
		||||
All sub-prefixes are defined as constants in the `git.grassecon.net/urdt-ussd/common` package. The constant names have the prefix `DATA_`
 | 
			
		||||
 | 
			
		||||
Please refer to inline godoc documentation for the `git.grassecon.net/urdt-ussd/common` package for details on each data item.
 | 
			
		||||
@ -1,13 +0,0 @@
 | 
			
		||||
package entry
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"git.defalsify.org/vise.git/resource"
 | 
			
		||||
	"git.defalsify.org/vise.git/persist"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type EntryHandler interface {
 | 
			
		||||
	Init(context.Context, string, []byte) (resource.Result, error) // HandlerFunc
 | 
			
		||||
	Exit()
 | 
			
		||||
	SetPersister(*persist.Persister)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										40
									
								
								env/load.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										40
									
								
								env/load.go
									
									
									
									
										vendored
									
									
								
							@ -1,40 +0,0 @@
 | 
			
		||||
package env
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/joho/godotenv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func LoadEnvVariables() {
 | 
			
		||||
	LoadEnvVariablesPath(".")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func LoadEnvVariablesPath(dir string) {
 | 
			
		||||
	fp := path.Join(dir, ".env")
 | 
			
		||||
	err := godotenv.Load(fp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal("Error loading .env file", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper to get environment variables with a default fallback
 | 
			
		||||
func GetEnv(key, defaultVal string) string {
 | 
			
		||||
	if value, exists := os.LookupEnv(key); exists {
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
	return defaultVal
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helper to safely convert environment variables to uint
 | 
			
		||||
func GetEnvUint(key string, defaultVal uint) uint {
 | 
			
		||||
	if value, exists := os.LookupEnv(key); exists {
 | 
			
		||||
		if parsed, err := strconv.Atoi(value); err == nil && parsed >= 0 {
 | 
			
		||||
			return uint(parsed)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return defaultVal
 | 
			
		||||
}
 | 
			
		||||
@ -1,15 +0,0 @@
 | 
			
		||||
package errors
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrInvalidRequest = errors.New("invalid request for context")
 | 
			
		||||
	ErrSessionMissing = errors.New("missing session")
 | 
			
		||||
	ErrInvalidInput   = errors.New("invalid input")
 | 
			
		||||
	ErrStorage        = errors.New("storage retrieval fail")
 | 
			
		||||
	ErrEngineType     = errors.New("incompatible engine")
 | 
			
		||||
	ErrEngineInit     = errors.New("engine init fail")
 | 
			
		||||
	ErrEngineExec     = errors.New("engine exec fail")
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										31
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								go.mod
									
									
									
									
									
								
							@ -1,39 +1,28 @@
 | 
			
		||||
module git.grassecon.net/grassrootseconomics/visedriver
 | 
			
		||||
module git.grassecon.net/urdt/ussd
 | 
			
		||||
 | 
			
		||||
go 1.23.0
 | 
			
		||||
go 1.22.6
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d
 | 
			
		||||
	git.defalsify.org/vise.git v0.1.0-rc.3.0.20240911162138-1f2af8672dc7
 | 
			
		||||
	github.com/alecthomas/assert/v2 v2.2.2
 | 
			
		||||
	github.com/gofrs/uuid v4.4.0+incompatible
 | 
			
		||||
	github.com/grassrootseconomics/eth-custodial v1.3.0-beta
 | 
			
		||||
	github.com/grassrootseconomics/ussd-data-service v1.2.0-beta
 | 
			
		||||
	github.com/jackc/pgx/v5 v5.7.1
 | 
			
		||||
	github.com/joho/godotenv v1.5.1
 | 
			
		||||
	github.com/peteole/testdata-loader v0.3.0
 | 
			
		||||
	github.com/stretchr/testify v1.9.0
 | 
			
		||||
	golang.org/x/crypto v0.27.0
 | 
			
		||||
	gopkg.in/leonelquinteros/gotext.v1 v1.3.1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require gopkg.in/dnaeon/go-vcr.v4 v4.0.1 // indirect
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/alecthomas/participle/v2 v2.0.0 // indirect
 | 
			
		||||
	github.com/alecthomas/repr v0.2.0 // indirect
 | 
			
		||||
	github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 | 
			
		||||
	github.com/davecgh/go-spew v1.1.1 // indirect
 | 
			
		||||
	github.com/fxamacker/cbor/v2 v2.4.0 // indirect
 | 
			
		||||
	github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 // indirect
 | 
			
		||||
	github.com/hexops/gotextdiff v1.0.3 // indirect
 | 
			
		||||
	github.com/jackc/pgpassfile v1.0.0 // indirect
 | 
			
		||||
	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
 | 
			
		||||
	github.com/jackc/puddle/v2 v2.2.2 // indirect
 | 
			
		||||
	github.com/kr/text v0.2.0 // indirect
 | 
			
		||||
	github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 | 
			
		||||
	github.com/rogpeppe/go-internal v1.13.1 // indirect
 | 
			
		||||
	github.com/pmezard/go-difflib v1.0.0 // indirect
 | 
			
		||||
	github.com/stretchr/objx v0.5.2 // indirect
 | 
			
		||||
	github.com/stretchr/testify v1.9.0
 | 
			
		||||
	github.com/x448/float16 v0.8.4 // indirect
 | 
			
		||||
	golang.org/x/sync v0.8.0 // indirect
 | 
			
		||||
	golang.org/x/sys v0.25.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.18.0 // indirect
 | 
			
		||||
	gopkg.in/leonelquinteros/gotext.v1 v1.3.1 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										58
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								go.sum
									
									
									
									
									
								
							@ -1,76 +1,38 @@
 | 
			
		||||
git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d h1:bPAOVZOX4frSGhfOdcj7kc555f8dc9DmMd2YAyC2AMw=
 | 
			
		||||
git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 | 
			
		||||
git.defalsify.org/vise.git v0.1.0-rc.3.0.20240911162138-1f2af8672dc7 h1:embPZDx0Sgpq6jp9vcZ1GVI0eum3PsPCmAfxAa/1KLI=
 | 
			
		||||
git.defalsify.org/vise.git v0.1.0-rc.3.0.20240911162138-1f2af8672dc7/go.mod h1:JDguWmcoWBdsnpw7PUjVZAEpdC/ubBmjdUBy3tjP63M=
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
 | 
			
		||||
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
 | 
			
		||||
github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
 | 
			
		||||
github.com/alecthomas/participle/v2 v2.0.0/go.mod h1:rAKZdJldHu8084ojcWevWAL8KmEU+AT+Olodb+WoN2Y=
 | 
			
		||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
 | 
			
		||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
 | 
			
		||||
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c h1:H9Nm+I7Cg/YVPpEV1RzU3Wq2pjamPc/UtHDgItcb7lE=
 | 
			
		||||
github.com/barbashov/iso639-3 v0.0.0-20211020172741-1f4ffb2d8d1c/go.mod h1:rGod7o6KPeJ+hyBpHfhi4v7blx9sf+QsHsA7KAsdN6U=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
 | 
			
		||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
 | 
			
		||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
 | 
			
		||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 | 
			
		||||
github.com/grassrootseconomics/eth-custodial v1.3.0-beta h1:twrMBhl89GqDUL9PlkzQxMP/6OST1BByrNDj+rqXDmU=
 | 
			
		||||
github.com/grassrootseconomics/eth-custodial v1.3.0-beta/go.mod h1:7uhRcdnJplX4t6GKCEFkbeDhhjlcaGJeJqevbcvGLZo=
 | 
			
		||||
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta h1:fn1gwbWIwHVEBtUC2zi5OqTlfI/5gU1SMk0fgGixIXk=
 | 
			
		||||
github.com/grassrootseconomics/ussd-data-service v1.2.0-beta/go.mod h1:omfI0QtUwIdpu9gMcUqLMCG8O1XWjqJGBx1qUMiGWC0=
 | 
			
		||||
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4 h1:U4kkNYryi/qfbBF8gh7Vsbuz+cVmhf5kt6pE9bYYyLo=
 | 
			
		||||
github.com/graygnuorg/go-gdbm v0.0.0-20220711140707-71387d66dce4/go.mod h1:zpZDgZFzeq9s0MIeB1P50NIEWDFFHSFBohI/NbaTD/Y=
 | 
			
		||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 | 
			
		||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 | 
			
		||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 | 
			
		||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
 | 
			
		||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
 | 
			
		||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
 | 
			
		||||
github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs=
 | 
			
		||||
github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA=
 | 
			
		||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
 | 
			
		||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
 | 
			
		||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
 | 
			
		||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 | 
			
		||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 | 
			
		||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 | 
			
		||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
			
		||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
			
		||||
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a h1:0Q3H0YXzMHiciXtRcM+j0jiCe8WKPQHoRgQiRTnfcLY=
 | 
			
		||||
github.com/mattn/kinako v0.0.0-20170717041458-332c0a7e205a/go.mod h1:CdTTBOYzS5E4mWS1N8NWP6AHI19MP0A2B18n3hLzRMk=
 | 
			
		||||
github.com/pashagolub/pgxmock/v4 v4.3.0 h1:DqT7fk0OCK6H0GvqtcMsLpv8cIwWqdxWgfZNLeHCb/s=
 | 
			
		||||
github.com/pashagolub/pgxmock/v4 v4.3.0/go.mod h1:9VoVHXwS3XR/yPtKGzwQvwZX1kzGB9sM8SviDcHDa3A=
 | 
			
		||||
github.com/peteole/testdata-loader v0.3.0 h1:8jckE9KcyNHgyv/VPoaljvKZE0Rqr8+dPVYH6rfNr9I=
 | 
			
		||||
github.com/peteole/testdata-loader v0.3.0/go.mod h1:Mt0ZbRtb56u8SLJpNP+BnQbENljMorYBpqlvt3cS83U=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
 | 
			
		||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
			
		||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 | 
			
		||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 | 
			
		||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
 | 
			
		||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 | 
			
		||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
 | 
			
		||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
 | 
			
		||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
 | 
			
		||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 | 
			
		||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 | 
			
		||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
			
		||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
 | 
			
		||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
 | 
			
		||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
 | 
			
		||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 | 
			
		||||
gopkg.in/dnaeon/go-vcr.v4 v4.0.1 h1:dIFuOqqDZIJ9BTcK+DXmElzypQ6PV9fBQZSIwY+J1yM=
 | 
			
		||||
gopkg.in/dnaeon/go-vcr.v4 v4.0.1/go.mod h1:65yxh9goQVrudqofKtHA4JNFWd6XZRkWfKN4YpMx7KI=
 | 
			
		||||
gopkg.in/leonelquinteros/gotext.v1 v1.3.1 h1:8d9/fdTG0kn/B7NNGV1BsEyvektXFAbkMsTZS2sFSCc=
 | 
			
		||||
gopkg.in/leonelquinteros/gotext.v1 v1.3.1/go.mod h1:X1WlGDeAFIYsW6GjgMm4VwUwZ2XjI7Zan2InxSUQWrU=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
			
		||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										104
									
								
								internal/handlers/server/accountservice.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								internal/handlers/server/accountservice.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
			
		||||
package server
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/config"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AccountServiceInterface interface {
 | 
			
		||||
	CheckBalance(publicKey string) (string, error)
 | 
			
		||||
	CreateAccount() (*models.AccountResponse, error)
 | 
			
		||||
	CheckAccountStatus(trackingId string) (string, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AccountService struct {
 | 
			
		||||
	Client *http.Client
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckAccountStatus retrieves the status of an account transaction based on the provided tracking ID.
 | 
			
		||||
//
 | 
			
		||||
// 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(trackingId string) (string, error) {
 | 
			
		||||
	resp, err := as.Client.Get(config.TrackStatusURL + trackingId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var trackResp models.TrackStatusResponse
 | 
			
		||||
	err = json.Unmarshal(body, &trackResp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	status := trackResp.Result.Transaction.Status
 | 
			
		||||
 | 
			
		||||
	return status, 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(publicKey string) (string, error) {
 | 
			
		||||
	resp, err := http.Get(config.BalanceURL + publicKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "0.0", err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "0.0", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var balanceResp models.BalanceResponse
 | 
			
		||||
	err = json.Unmarshal(body, &balanceResp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "0.0", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	balance := balanceResp.Result.Balance
 | 
			
		||||
	return balance, 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() (*models.AccountResponse, error) {
 | 
			
		||||
	resp, err := as.Client.Post(config.CreateAccountURL, "application/json", nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	body, err := io.ReadAll(resp.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var accountResp models.AccountResponse
 | 
			
		||||
	err = json.Unmarshal(body, &accountResp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &accountResp, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1036
									
								
								internal/handlers/ussd/menuhandler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1036
									
								
								internal/handlers/ussd/menuhandler.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1773
									
								
								internal/handlers/ussd/menuhandler_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1773
									
								
								internal/handlers/ussd/menuhandler_test.go
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										59
									
								
								internal/handlers/ussd/mocks/dbmock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								internal/handlers/ussd/mocks/dbmock.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/lang"
 | 
			
		||||
	"github.com/stretchr/testify/mock"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockDb struct {
 | 
			
		||||
	mock.Mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) SetPrefix(prefix uint8) {
 | 
			
		||||
	m.Called(prefix)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Prefix() uint8 {
 | 
			
		||||
	args := m.Called()
 | 
			
		||||
	return args.Get(0).(uint8)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Safe() bool {
 | 
			
		||||
	args := m.Called()
 | 
			
		||||
	return args.Get(0).(bool)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) SetLanguage(language *lang.Language) {
 | 
			
		||||
	m.Called(language)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) SetLock(uint8, bool) error {
 | 
			
		||||
	args := m.Called()
 | 
			
		||||
	return args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Connect(ctx context.Context, connectionStr string) error {
 | 
			
		||||
	args := m.Called(ctx, connectionStr)
 | 
			
		||||
	return args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) SetSession(sessionId string) {
 | 
			
		||||
	m.Called(sessionId)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Put(ctx context.Context, key, value []byte) error {
 | 
			
		||||
	args := m.Called(ctx, key, value)
 | 
			
		||||
	return args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Get(ctx context.Context, key []byte) ([]byte, error) {
 | 
			
		||||
	args := m.Called(ctx, key)
 | 
			
		||||
	return nil, args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockDb) Close() error {
 | 
			
		||||
	args := m.Called(nil)
 | 
			
		||||
	return args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								internal/handlers/ussd/mocks/servicemock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								internal/handlers/ussd/mocks/servicemock.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/models"
 | 
			
		||||
	"github.com/stretchr/testify/mock"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MockAccountService implements AccountServiceInterface for testing
 | 
			
		||||
type MockAccountService struct {
 | 
			
		||||
	mock.Mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) {
 | 
			
		||||
	args := m.Called()
 | 
			
		||||
	return args.Get(0).(*models.AccountResponse), args.Error(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockAccountService) CheckBalance(publicKey string) (string, error) {
 | 
			
		||||
	args := m.Called(publicKey)
 | 
			
		||||
	return args.String(0), args.Error(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockAccountService) CheckAccountStatus(trackingId string) (string, error) {
 | 
			
		||||
	args := m.Called(trackingId)
 | 
			
		||||
	return args.String(0), args.Error(1)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								internal/handlers/ussd/mocks/userdbmock.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								internal/handlers/ussd/mocks/userdbmock.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/utils"
 | 
			
		||||
	"github.com/stretchr/testify/mock"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type MockUserDataStore struct {
 | 
			
		||||
	db.Db
 | 
			
		||||
	mock.Mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) {
 | 
			
		||||
	args := m.Called(ctx, sessionId, typ)
 | 
			
		||||
	return args.Get(0).([]byte), args.Error(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ utils.DataTyp, value []byte) error {
 | 
			
		||||
	args := m.Called(ctx, sessionId, typ, value)
 | 
			
		||||
	return args.Error(0)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								internal/http/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								internal/http/server.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,150 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
	"git.defalsify.org/vise.git/engine"
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
	"git.defalsify.org/vise.git/persist"
 | 
			
		||||
	"git.defalsify.org/vise.git/resource"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg = logging.NewVanilla().WithDomain("httpserver")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RequestParser interface {
 | 
			
		||||
	GetSessionId(rq *http.Request) (string, error)
 | 
			
		||||
	GetInput(rq *http.Request) ([]byte, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DefaultRequestParser struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(rp *DefaultRequestParser) GetSessionId(rq *http.Request) (string, error) {
 | 
			
		||||
	v := rq.Header.Get("X-Vise-Session")
 | 
			
		||||
	if v == "" {
 | 
			
		||||
		return "", fmt.Errorf("no session found")
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(rp *DefaultRequestParser) GetInput(rq *http.Request) ([]byte, error) {
 | 
			
		||||
	defer rq.Body.Close()
 | 
			
		||||
	v, err := ioutil.ReadAll(rq.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SessionHandler struct {
 | 
			
		||||
	cfgTemplate engine.Config
 | 
			
		||||
	rp RequestParser
 | 
			
		||||
	rs resource.Resource
 | 
			
		||||
	//first resource.EntryFunc
 | 
			
		||||
	hn *ussd.Handlers
 | 
			
		||||
	provider StorageProvider
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//func NewSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, first resource.EntryFunc) *SessionHandler {
 | 
			
		||||
func NewSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *ussd.Handlers) *SessionHandler {
 | 
			
		||||
	return &SessionHandler{
 | 
			
		||||
		cfgTemplate: cfg,
 | 
			
		||||
		rs: rs,
 | 
			
		||||
		//first: first,
 | 
			
		||||
		hn: hn,
 | 
			
		||||
		rp: rp,
 | 
			
		||||
		provider: NewSimpleStorageProvider(stateDb, userdataDb),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *SessionHandler) writeError(w http.ResponseWriter, code int, msg string, err error) {
 | 
			
		||||
	w.Header().Set("X-Vise", msg + ": " + err.Error())
 | 
			
		||||
	w.Header().Set("Content-Length", "0")
 | 
			
		||||
	w.WriteHeader(code)
 | 
			
		||||
	_, err = w.Write([]byte{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		w.WriteHeader(500)
 | 
			
		||||
		w.Header().Set("X-Vise", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f* SessionHandler) Shutdown() {
 | 
			
		||||
	err := f.provider.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.Errorf("handler shutdown error", "err", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	var r bool
 | 
			
		||||
	sessionId, err := f.rp.GetSessionId(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 400, "Session missing", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	input, err := f.rp.GetInput(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 400, "Input read fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	ctx := req.Context()
 | 
			
		||||
	cfg := f.cfgTemplate
 | 
			
		||||
	cfg.SessionId = sessionId
 | 
			
		||||
 | 
			
		||||
	logg.InfoCtxf(ctx, "new request",  "session", cfg.SessionId, "input", input)
 | 
			
		||||
 | 
			
		||||
	storage, err := f.provider.Get(cfg.SessionId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 500, "Storage retrieval fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	f.hn = f.hn.WithPersister(storage.Persister)
 | 
			
		||||
	defer f.provider.Put(cfg.SessionId, storage)
 | 
			
		||||
	en := getEngine(cfg, f.rs, storage.Persister)
 | 
			
		||||
	en = en.WithFirst(f.hn.Init)
 | 
			
		||||
	if cfg.EngineDebug {
 | 
			
		||||
		en = en.WithDebug(nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r, err = en.Init(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 500, "Engine init fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if r && len(input) > 0 {
 | 
			
		||||
		r, err = en.Exec(ctx, input)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 500, "Engine exec fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.WriteHeader(200)
 | 
			
		||||
	w.Header().Set("Content-Type", "text/plain")
 | 
			
		||||
	_, err = en.WriteResult(ctx, w)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 500, "Write result fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = en.Finish()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f.writeError(w, 500, "Engine finish fail", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_ = r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) *engine.DefaultEngine {
 | 
			
		||||
	en := engine.NewEngine(cfg, rs)
 | 
			
		||||
	en = en.WithPersister(pr)
 | 
			
		||||
	return en
 | 
			
		||||
}
 | 
			
		||||
@ -1,45 +1,41 @@
 | 
			
		||||
package storage
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
	"git.defalsify.org/vise.git/persist"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DATATYPE_EXTEND = 128
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Storage struct {
 | 
			
		||||
	Persister *persist.Persister
 | 
			
		||||
	UserdataDb db.Db	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StorageProvider interface {
 | 
			
		||||
	Get(sessionId string) (*Storage, error)
 | 
			
		||||
	Put(sessionId string, storage *Storage) error
 | 
			
		||||
	Get(sessionId string) (Storage, error)
 | 
			
		||||
	Put(sessionId string, storage Storage) error
 | 
			
		||||
	Close() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SimpleStorageProvider struct {
 | 
			
		||||
	*Storage
 | 
			
		||||
	Storage
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSimpleStorageProvider(stateStore db.Db, userdataStore db.Db) StorageProvider {
 | 
			
		||||
	pe := persist.NewPersister(stateStore)
 | 
			
		||||
	pe = pe.WithFlush()
 | 
			
		||||
	return &SimpleStorageProvider{
 | 
			
		||||
		Storage: &Storage{
 | 
			
		||||
		Storage: Storage{
 | 
			
		||||
			Persister: pe,
 | 
			
		||||
			UserdataDb: userdataStore,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *SimpleStorageProvider) Get(sessionId string) (*Storage, error) {
 | 
			
		||||
func (p *SimpleStorageProvider) Get(sessionId string) (Storage, error) {
 | 
			
		||||
	return p.Storage, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *SimpleStorageProvider) Put(sessionId string, storage *Storage) error {
 | 
			
		||||
func (p *SimpleStorageProvider) Put(sessionId string, storage Storage) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								internal/models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								internal/models/accountresponse.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type AccountResponse struct {
 | 
			
		||||
	Ok     bool `json:"ok"`
 | 
			
		||||
	Result struct {
 | 
			
		||||
		CustodialId json.Number `json:"custodialId"`
 | 
			
		||||
		PublicKey   string      `json:"publicKey"`
 | 
			
		||||
		TrackingId  string      `json:"trackingId"`
 | 
			
		||||
	} `json:"result"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								internal/models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								internal/models/balanceresponse.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								internal/models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								internal/models/trackstatusresponse.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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"`
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								internal/utils/age.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/utils/age.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import "time"
 | 
			
		||||
 | 
			
		||||
// CalculateAge calculates the age based on a given birthdate and the current date in the format dd/mm/yy
 | 
			
		||||
// It adjusts for cases where the current date is before the birthday in the current year.
 | 
			
		||||
func CalculateAge(birthdate, today time.Time) int {
 | 
			
		||||
	today = today.In(birthdate.Location())
 | 
			
		||||
	ty, tm, td := today.Date()
 | 
			
		||||
	today = time.Date(ty, tm, td, 0, 0, 0, 0, time.UTC)
 | 
			
		||||
	by, bm, bd := birthdate.Date()
 | 
			
		||||
	birthdate = time.Date(by, bm, bd, 0, 0, 0, 0, time.UTC)
 | 
			
		||||
	if today.Before(birthdate) {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	age := ty - by
 | 
			
		||||
	anniversary := birthdate.AddDate(age, 0, 0)
 | 
			
		||||
	if anniversary.After(today) {
 | 
			
		||||
		age--
 | 
			
		||||
	}
 | 
			
		||||
	return age
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CalculateAgeWithYOB calculates the age based on the given year of birth (YOB).
 | 
			
		||||
// It subtracts the YOB from the current year to determine the age.
 | 
			
		||||
//
 | 
			
		||||
// Parameters:
 | 
			
		||||
//   yob: The year of birth as an integer.
 | 
			
		||||
//
 | 
			
		||||
// Returns:
 | 
			
		||||
//   The calculated age as an integer.
 | 
			
		||||
func CalculateAgeWithYOB(yob int) int {
 | 
			
		||||
    currentYear := time.Now().Year()
 | 
			
		||||
    return currentYear - yob
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								internal/utils/db.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/utils/db.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DataTyp uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DATA_ACCOUNT DataTyp = iota
 | 
			
		||||
	DATA_ACCOUNT_CREATED
 | 
			
		||||
	DATA_TRACKING_ID
 | 
			
		||||
	DATA_PUBLIC_KEY
 | 
			
		||||
	DATA_CUSTODIAL_ID
 | 
			
		||||
	DATA_ACCOUNT_PIN
 | 
			
		||||
	DATA_ACCOUNT_STATUS
 | 
			
		||||
	DATA_FIRST_NAME
 | 
			
		||||
	DATA_FAMILY_NAME
 | 
			
		||||
	DATA_YOB
 | 
			
		||||
	DATA_LOCATION
 | 
			
		||||
	DATA_GENDER
 | 
			
		||||
	DATA_OFFERINGS
 | 
			
		||||
	DATA_RECIPIENT
 | 
			
		||||
	DATA_AMOUNT
 | 
			
		||||
	DATA_TEMPORARY_PIN
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func typToBytes(typ DataTyp) []byte {
 | 
			
		||||
	var b [2]byte
 | 
			
		||||
	binary.BigEndian.PutUint16(b[:], uint16(typ))
 | 
			
		||||
	return b[:]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PackKey(typ DataTyp, data []byte) []byte {
 | 
			
		||||
	v := typToBytes(typ)
 | 
			
		||||
	return append(v, data...)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								internal/utils/userStore.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								internal/utils/userStore.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
package utils
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/db"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DataStore interface {
 | 
			
		||||
	db.Db
 | 
			
		||||
	ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error)
 | 
			
		||||
	WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserDataStore struct {
 | 
			
		||||
	db.Db
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadEntry retrieves an entry from the store based on the provided parameters.
 | 
			
		||||
func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) {
 | 
			
		||||
	store.SetPrefix(db.DATATYPE_USERDATA)
 | 
			
		||||
	store.SetSession(sessionId)
 | 
			
		||||
	k := PackKey(typ, []byte(sessionId))
 | 
			
		||||
	return store.Get(ctx, k)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error {
 | 
			
		||||
	store.SetPrefix(db.DATATYPE_USERDATA)
 | 
			
		||||
	store.SetSession(sessionId)
 | 
			
		||||
	k := PackKey(typ, []byte(sessionId))
 | 
			
		||||
	return store.Put(ctx, k, value)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								request/base.go
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								request/base.go
									
									
									
									
									
								
							@ -1,110 +0,0 @@
 | 
			
		||||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"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/grassrootseconomics/visedriver/storage"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/errors"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/entry"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BaseRequestHandler struct {
 | 
			
		||||
	cfgTemplate engine.Config
 | 
			
		||||
	rp RequestParser
 | 
			
		||||
	rs resource.Resource
 | 
			
		||||
	hn entry.EntryHandler
 | 
			
		||||
	provider storage.StorageProvider
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//func NewBaseRequestHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp request.RequestParser, hn *handlers.Handlers) *BaseRequestHandler {
 | 
			
		||||
func NewBaseRequestHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn entry.EntryHandler) *BaseRequestHandler {
 | 
			
		||||
	return &BaseRequestHandler{
 | 
			
		||||
		cfgTemplate: cfg,
 | 
			
		||||
		rs:          rs,
 | 
			
		||||
		hn:          hn,
 | 
			
		||||
		rp:          rp,
 | 
			
		||||
		provider:    storage.NewSimpleStorageProvider(stateDb, userdataDb),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *BaseRequestHandler) Shutdown() {
 | 
			
		||||
	err := f.provider.Close()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.Errorf("handler shutdown error", "err", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *BaseRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) engine.Engine {
 | 
			
		||||
	en := engine.NewEngine(cfg, rs)
 | 
			
		||||
	en = en.WithPersister(pr)
 | 
			
		||||
	return en
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *BaseRequestHandler) Process(rqs RequestSession) (RequestSession, error) {
 | 
			
		||||
	var r bool
 | 
			
		||||
	var err error
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
	logg.InfoCtxf(rqs.Ctx, "new request", "data", rqs)
 | 
			
		||||
 | 
			
		||||
	rqs.Storage, err = f.provider.Get(rqs.Config.SessionId)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.ErrorCtxf(rqs.Ctx, "", "storage get error", err)
 | 
			
		||||
		return rqs, errors.ErrStorage
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//f.hn = f.hn.WithPersister(rqs.Storage.Persister)
 | 
			
		||||
	f.hn.SetPersister(rqs.Storage.Persister)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		f.hn.Exit()
 | 
			
		||||
	}()
 | 
			
		||||
	eni := f.GetEngine(rqs.Config, f.rs, rqs.Storage.Persister)
 | 
			
		||||
	en, ok := eni.(*engine.DefaultEngine)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		perr := f.provider.Put(rqs.Config.SessionId, rqs.Storage)
 | 
			
		||||
		rqs.Storage = nil
 | 
			
		||||
		if perr != nil {
 | 
			
		||||
			logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
 | 
			
		||||
		}
 | 
			
		||||
		return rqs, errors.ErrEngineType
 | 
			
		||||
	}
 | 
			
		||||
	en = en.WithFirst(f.hn.Init)
 | 
			
		||||
	if rqs.Config.EngineDebug {
 | 
			
		||||
		en = en.WithDebug(nil)
 | 
			
		||||
	}
 | 
			
		||||
	rqs.Engine = en
 | 
			
		||||
 | 
			
		||||
	r, err = rqs.Engine.Exec(rqs.Ctx, rqs.Input)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		perr := f.provider.Put(rqs.Config.SessionId, rqs.Storage)
 | 
			
		||||
		rqs.Storage = nil
 | 
			
		||||
		if perr != nil {
 | 
			
		||||
			logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
 | 
			
		||||
		}
 | 
			
		||||
		return rqs, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rqs.Continue = r
 | 
			
		||||
	return rqs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *BaseRequestHandler) Output(rqs RequestSession) (RequestSession,  error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	_, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer)
 | 
			
		||||
	return rqs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *BaseRequestHandler) Reset(rqs RequestSession) (RequestSession, error) {
 | 
			
		||||
	defer f.provider.Put(rqs.Config.SessionId, rqs.Storage)
 | 
			
		||||
	return rqs, rqs.Engine.Finish()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *BaseRequestHandler) GetConfig() engine.Config {
 | 
			
		||||
	return f.cfgTemplate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func(f *BaseRequestHandler) GetRequestParser() RequestParser {
 | 
			
		||||
	return f.rp
 | 
			
		||||
}
 | 
			
		||||
@ -1,37 +0,0 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DefaultRequestParser struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rp *DefaultRequestParser) GetSessionId(ctx context.Context, rq any) (string, error) {
 | 
			
		||||
	rqv, ok := rq.(*http.Request)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", errors.ErrInvalidRequest
 | 
			
		||||
	}
 | 
			
		||||
	v := rqv.Header.Get("X-Vise-Session")
 | 
			
		||||
	if v == "" {
 | 
			
		||||
		return "", errors.ErrSessionMissing
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) {
 | 
			
		||||
	rqv, ok := rq.(*http.Request)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.ErrInvalidRequest
 | 
			
		||||
	}
 | 
			
		||||
	defer rqv.Body.Close()
 | 
			
		||||
	v, err := ioutil.ReadAll(rqv.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
@ -1,92 +0,0 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/request"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg = logging.NewVanilla().WithDomain("visedriver.http.session")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// HTTPRequestHandler implements the session handler for HTTP
 | 
			
		||||
type HTTPRequestHandler struct {
 | 
			
		||||
	request.RequestHandler
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *HTTPRequestHandler) WriteError(w http.ResponseWriter, code int, err error) {
 | 
			
		||||
	s := err.Error()
 | 
			
		||||
	w.Header().Set("Content-Length", strconv.Itoa(len(s)))
 | 
			
		||||
	w.WriteHeader(code)
 | 
			
		||||
	_, err = w.Write([]byte(s))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.Errorf("error writing error!!", "err", err, "olderr", s)
 | 
			
		||||
		w.WriteHeader(500)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewHTTPRequestHandler(h request.RequestHandler) *HTTPRequestHandler {
 | 
			
		||||
	return &HTTPRequestHandler{
 | 
			
		||||
		RequestHandler: h,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (hh *HTTPRequestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
			
		||||
	var code int
 | 
			
		||||
	var err error
 | 
			
		||||
	var perr error
 | 
			
		||||
 | 
			
		||||
	rqs := request.RequestSession{
 | 
			
		||||
		Ctx:    req.Context(),
 | 
			
		||||
		Writer: w,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rp := hh.GetRequestParser()
 | 
			
		||||
	cfg := hh.GetConfig()
 | 
			
		||||
	cfg.SessionId, err = rp.GetSessionId(req.Context(), req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
 | 
			
		||||
		hh.WriteError(w, 400, err)
 | 
			
		||||
	}
 | 
			
		||||
	rqs.Config = cfg
 | 
			
		||||
	rqs.Input, err = rp.GetInput(req)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
 | 
			
		||||
		hh.WriteError(w, 400, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rqs, err = hh.Process(rqs)
 | 
			
		||||
	switch err {
 | 
			
		||||
	case errors.ErrStorage:
 | 
			
		||||
		code = 500
 | 
			
		||||
	case errors.ErrEngineInit:
 | 
			
		||||
		code = 500
 | 
			
		||||
	case errors.ErrEngineExec:
 | 
			
		||||
		code = 500
 | 
			
		||||
	default:
 | 
			
		||||
		code = 200
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if code != 200 {
 | 
			
		||||
		hh.WriteError(w, 500, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.WriteHeader(200)
 | 
			
		||||
	w.Header().Set("Content-Type", "text/plain")
 | 
			
		||||
	rqs, err = hh.Output(rqs)
 | 
			
		||||
	rqs, perr = hh.Reset(rqs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		hh.WriteError(w, 500, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if perr != nil {
 | 
			
		||||
		hh.WriteError(w, 500, perr)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,233 +0,0 @@
 | 
			
		||||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/engine"
 | 
			
		||||
	viseerrors "git.grassecon.net/grassrootseconomics/visedriver/errors"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/testutil/mocks/httpmocks"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/request"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// invalidRequestType is a custom type to test invalid request scenarios
 | 
			
		||||
type invalidRequestType struct{}
 | 
			
		||||
 | 
			
		||||
// errorReader is a helper type that always returns an error when Read is called
 | 
			
		||||
type errorReader struct{}
 | 
			
		||||
 | 
			
		||||
func (e *errorReader) Read(p []byte) (n int, err error) {
 | 
			
		||||
	return 0, errors.New("read error")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRequestHandler_ServeHTTP(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name           string
 | 
			
		||||
		sessionID      string
 | 
			
		||||
		input          []byte
 | 
			
		||||
		parserErr      error
 | 
			
		||||
		processErr     error
 | 
			
		||||
		outputErr      error
 | 
			
		||||
		resetErr       error
 | 
			
		||||
		expectedStatus int
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:           "Success",
 | 
			
		||||
			sessionID:      "123",
 | 
			
		||||
			input:          []byte("test input"),
 | 
			
		||||
			expectedStatus: http.StatusOK,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "Missing Session ID",
 | 
			
		||||
			sessionID:      "",
 | 
			
		||||
			parserErr:      viseerrors.ErrSessionMissing,
 | 
			
		||||
			expectedStatus: http.StatusBadRequest,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "Process Error",
 | 
			
		||||
			sessionID:      "123",
 | 
			
		||||
			input:          []byte("test input"),
 | 
			
		||||
			processErr:     viseerrors.ErrStorage,
 | 
			
		||||
			expectedStatus: http.StatusInternalServerError,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "Output Error",
 | 
			
		||||
			sessionID:      "123",
 | 
			
		||||
			input:          []byte("test input"),
 | 
			
		||||
			outputErr:      errors.New("output error"),
 | 
			
		||||
			expectedStatus: http.StatusOK,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:           "Reset Error",
 | 
			
		||||
			sessionID:      "123",
 | 
			
		||||
			input:          []byte("test input"),
 | 
			
		||||
			resetErr:       errors.New("reset error"),
 | 
			
		||||
			expectedStatus: http.StatusOK,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			mockRequestParser := &httpmocks.MockRequestParser{
 | 
			
		||||
				GetSessionIdFunc: func(any) (string, error) {
 | 
			
		||||
					return tt.sessionID, tt.parserErr
 | 
			
		||||
				},
 | 
			
		||||
				GetInputFunc: func(any) ([]byte, error) {
 | 
			
		||||
					return tt.input, nil
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			mockRequestHandler := &httpmocks.MockRequestHandler{
 | 
			
		||||
				ProcessFunc: func(rs request.RequestSession) (request.RequestSession, error) {
 | 
			
		||||
					return rs, tt.processErr
 | 
			
		||||
				},
 | 
			
		||||
				OutputFunc: func(rs request.RequestSession) (request.RequestSession, error) {
 | 
			
		||||
					return rs, tt.outputErr
 | 
			
		||||
				},
 | 
			
		||||
				ResetFunc: func(rs request.RequestSession) (request.RequestSession, error) {
 | 
			
		||||
					return rs, tt.resetErr
 | 
			
		||||
				},
 | 
			
		||||
				GetRequestParserFunc: func() request.RequestParser {
 | 
			
		||||
					return mockRequestParser
 | 
			
		||||
				},
 | 
			
		||||
				GetConfigFunc: func() engine.Config {
 | 
			
		||||
					return engine.Config{}
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			sessionHandler := &HTTPRequestHandler{
 | 
			
		||||
				RequestHandler: mockRequestHandler,
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(tt.input))
 | 
			
		||||
			req.Header.Set("X-Vise-Session", tt.sessionID)
 | 
			
		||||
 | 
			
		||||
			rr := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
			sessionHandler.ServeHTTP(rr, req)
 | 
			
		||||
 | 
			
		||||
			if status := rr.Code; status != tt.expectedStatus {
 | 
			
		||||
				t.Errorf("handler returned wrong status code: got %v want %v",
 | 
			
		||||
					status, tt.expectedStatus)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRequestHandler_WriteError(t *testing.T) {
 | 
			
		||||
	handler := &HTTPRequestHandler{}
 | 
			
		||||
	mockWriter := &httpmocks.MockWriter{}
 | 
			
		||||
	err := errors.New("test error")
 | 
			
		||||
 | 
			
		||||
	handler.WriteError(mockWriter, http.StatusBadRequest, err)
 | 
			
		||||
 | 
			
		||||
	if mockWriter.WrittenString != "" {
 | 
			
		||||
		t.Errorf("Expected empty body, got %s", mockWriter.WrittenString)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDefaultRequestParser_GetSessionId(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name          string
 | 
			
		||||
		request       any
 | 
			
		||||
		expectedID    string
 | 
			
		||||
		expectedError error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid Session ID",
 | 
			
		||||
			request: func() *http.Request {
 | 
			
		||||
				req := httptest.NewRequest(http.MethodPost, "/", nil)
 | 
			
		||||
				req.Header.Set("X-Vise-Session", "123456")
 | 
			
		||||
				return req
 | 
			
		||||
			}(),
 | 
			
		||||
			expectedID:    "123456",
 | 
			
		||||
			expectedError: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:          "Missing Session ID",
 | 
			
		||||
			request:       httptest.NewRequest(http.MethodPost, "/", nil),
 | 
			
		||||
			expectedID:    "",
 | 
			
		||||
			expectedError: viseerrors.ErrSessionMissing,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:          "Invalid Request Type",
 | 
			
		||||
			request:       invalidRequestType{},
 | 
			
		||||
			expectedID:    "",
 | 
			
		||||
			expectedError: viseerrors.ErrInvalidRequest,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parser := &DefaultRequestParser{}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			id, err := parser.GetSessionId(context.Background(),tt.request)
 | 
			
		||||
 | 
			
		||||
			if id != tt.expectedID {
 | 
			
		||||
				t.Errorf("Expected session ID %s, got %s", tt.expectedID, id)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err != tt.expectedError {
 | 
			
		||||
				t.Errorf("Expected error %v, got %v", tt.expectedError, err)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDefaultRequestParser_GetInput(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name          string
 | 
			
		||||
		request       any
 | 
			
		||||
		expectedInput []byte
 | 
			
		||||
		expectedError error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "Valid Input",
 | 
			
		||||
			request: func() *http.Request {
 | 
			
		||||
				return httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString("test input"))
 | 
			
		||||
			}(),
 | 
			
		||||
			expectedInput: []byte("test input"),
 | 
			
		||||
			expectedError: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:          "Empty Input",
 | 
			
		||||
			request:       httptest.NewRequest(http.MethodPost, "/", nil),
 | 
			
		||||
			expectedInput: []byte{},
 | 
			
		||||
			expectedError: nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:          "Invalid Request Type",
 | 
			
		||||
			request:       invalidRequestType{},
 | 
			
		||||
			expectedInput: nil,
 | 
			
		||||
			expectedError: viseerrors.ErrInvalidRequest,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Read Error",
 | 
			
		||||
			request: func() *http.Request {
 | 
			
		||||
				return httptest.NewRequest(http.MethodPost, "/", &errorReader{})
 | 
			
		||||
			}(),
 | 
			
		||||
			expectedInput: nil,
 | 
			
		||||
			expectedError: errors.New("read error"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parser := &DefaultRequestParser{}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			input, err := parser.GetInput(tt.request)
 | 
			
		||||
 | 
			
		||||
			if !bytes.Equal(input, tt.expectedInput) {
 | 
			
		||||
				t.Errorf("Expected input %s, got %s", tt.expectedInput, input)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err != tt.expectedError && (err == nil || err.Error() != tt.expectedError.Error()) {
 | 
			
		||||
				t.Errorf("Expected error %v, got %v", tt.expectedError, err)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -1,42 +0,0 @@
 | 
			
		||||
package request
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"git.defalsify.org/vise.git/resource"
 | 
			
		||||
	"git.defalsify.org/vise.git/persist"
 | 
			
		||||
	"git.defalsify.org/vise.git/engine"
 | 
			
		||||
	"git.defalsify.org/vise.git/logging"
 | 
			
		||||
	"git.grassecon.net/grassrootseconomics/visedriver/storage"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	logg = logging.NewVanilla().WithDomain("visedriver.request")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RequestSession struct {
 | 
			
		||||
	Ctx context.Context
 | 
			
		||||
	Config engine.Config
 | 
			
		||||
	Engine engine.Engine
 | 
			
		||||
	Input []byte
 | 
			
		||||
	Storage *storage.Storage
 | 
			
		||||
	Writer io.Writer
 | 
			
		||||
	Continue bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: seems like can remove this.
 | 
			
		||||
type RequestParser interface {
 | 
			
		||||
	GetSessionId(ctx context.Context, rq any) (string, error)
 | 
			
		||||
	GetInput(rq any) ([]byte, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RequestHandler interface {
 | 
			
		||||
	GetConfig() engine.Config
 | 
			
		||||
	GetRequestParser() RequestParser
 | 
			
		||||
	GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine 
 | 
			
		||||
	Process(rs RequestSession) (RequestSession, error)
 | 
			
		||||
	Output(rs RequestSession) (RequestSession, error)
 | 
			
		||||
	Reset(rs RequestSession) (RequestSession, error)
 | 
			
		||||
	Shutdown()
 | 
			
		||||
}
 | 
			
		||||
@ -1,44 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "ok": true,
 | 
			
		||||
    "description": "Token holdings with current balances",
 | 
			
		||||
    "result": {
 | 
			
		||||
      "holdings": [
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee",
 | 
			
		||||
          "tokenSymbol": "FSPTST",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "8869964242"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0x724F2910D790B54A39a7638282a45B1D83564fFA",
 | 
			
		||||
          "tokenSymbol": "GEO",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "9884"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0x2105a206B7bec31E2F90acF7385cc8F7F5f9D273",
 | 
			
		||||
          "tokenSymbol": "MFNK",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "19788697"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0x63DE2Ac8D1008351Cc69Fb8aCb94Ba47728a7E83",
 | 
			
		||||
          "tokenSymbol": "MILO",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "75"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9",
 | 
			
		||||
          "tokenSymbol": "SOHAIL",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "27874115"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          "contractAddress": "0x45d747172e77d55575c197CbA9451bC2CD8F4958",
 | 
			
		||||
          "tokenSymbol": "SRF",
 | 
			
		||||
          "tokenDecimals": "6",
 | 
			
		||||
          "balance": "2745987"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
							
								
								
									
										17
									
								
								services/registration/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								services/registration/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
# Variables to match files in the current directory
 | 
			
		||||
INPUTS = $(wildcard ./*.vis)
 | 
			
		||||
TXTS = $(wildcard ./*.txt.orig)
 | 
			
		||||
 | 
			
		||||
# Rule to build .bin files from .vis files
 | 
			
		||||
%.vis:
 | 
			
		||||
	go run  ../../go-vise/dev/asm/main.go -f pp.csv $(basename $@).vis > $(basename $@).bin
 | 
			
		||||
	@echo "Built $(basename $@).bin from $(basename $@).vis"
 | 
			
		||||
 | 
			
		||||
# Rule to copy .orig files to .txt
 | 
			
		||||
%.txt.orig:
 | 
			
		||||
	cp -v $(basename $@).orig $(basename $@)
 | 
			
		||||
	@echo "Copied $(basename $@).orig to $(basename $@)"
 | 
			
		||||
 | 
			
		||||
# 'all' target depends on all .vis and .txt.orig files
 | 
			
		||||
all: $(INPUTS) $(TXTS)
 | 
			
		||||
	@echo "Running all: $(INPUTS) $(TXTS)"
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_creation
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_creation
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Your account is being created...
 | 
			
		||||
							
								
								
									
										4
									
								
								services/registration/account_creation.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								services/registration/account_creation.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
RELOAD verify_pin
 | 
			
		||||
CATCH create_pin_mismatch flag_pin_mismatch 1
 | 
			
		||||
LOAD quit 0
 | 
			
		||||
HALT
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_creation_failed
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_creation_failed
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Your account creation request failed. Please try again later.
 | 
			
		||||
							
								
								
									
										3
									
								
								services/registration/account_creation_failed.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/account_creation_failed.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
MOUT quit 9
 | 
			
		||||
HALT
 | 
			
		||||
INCMP quit 9
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_creation_failed_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_creation_failed_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Ombi lako la kusajiliwa haliwezi kukamilishwa. Tafadhali jaribu tena baadaye.
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_creation_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_creation_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Akaunti yako inatengenezwa...
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
My Account
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Akaunti yangu
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_pending
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_pending
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Your account is still being created.
 | 
			
		||||
							
								
								
									
										3
									
								
								services/registration/account_pending.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/account_pending.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
RELOAD check_account_status
 | 
			
		||||
CATCH main flag_account_success 1
 | 
			
		||||
HALT
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/account_pending_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/account_pending_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Akaunti yako bado inatengenezwa
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/address
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/address
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Address: {{.check_identifier}}
 | 
			
		||||
							
								
								
									
										6
									
								
								services/registration/address.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								services/registration/address.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
LOAD check_identifier 0
 | 
			
		||||
RELOAD check_identifier
 | 
			
		||||
MAP check_identifier
 | 
			
		||||
MOUT quit 9
 | 
			
		||||
HALT
 | 
			
		||||
INCMP quit 9
 | 
			
		||||
							
								
								
									
										2
									
								
								services/registration/amount
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/amount
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
Maximum amount: {{.max_amount}}
 | 
			
		||||
Enter amount:
 | 
			
		||||
							
								
								
									
										12
									
								
								services/registration/amount.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								services/registration/amount.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
LOAD reset_transaction_amount 0
 | 
			
		||||
LOAD max_amount 10
 | 
			
		||||
MAP max_amount
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
LOAD validate_amount 64
 | 
			
		||||
RELOAD validate_amount
 | 
			
		||||
CATCH invalid_amount flag_invalid_amount 1
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
LOAD get_recipient 12
 | 
			
		||||
LOAD get_sender 64
 | 
			
		||||
INCMP transaction_pin *
 | 
			
		||||
							
								
								
									
										2
									
								
								services/registration/amount_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/amount_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
Kiwango cha juu: {{.max_amount}}
 | 
			
		||||
Weka kiwango:
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/back_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/back_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Back
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/back_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/back_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Rudi
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/balances
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/balances
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Balances:
 | 
			
		||||
							
								
								
									
										8
									
								
								services/registration/balances.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								services/registration/balances.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
LOAD reset_account_authorized 0
 | 
			
		||||
MOUT my_balance 1
 | 
			
		||||
MOUT community_balance 2
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
INCMP my_balance 1
 | 
			
		||||
INCMP community_balance 2
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/balances_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/balances_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Salio
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/change_language_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/change_language_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Change language
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/change_language_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/change_language_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Badili lugha
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/change_pin_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/change_pin_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Change PIN
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/change_pin_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/change_pin_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Badili PIN
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/check_balance_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/check_balance_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Check balances
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/check_balance_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/check_balance_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Angalia salio
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/check_statement_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/check_statement_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Check statement
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/check_statement_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/check_statement_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Taarifa ya matumizi
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/comminity_balance_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/comminity_balance_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Salio la kikundi
 | 
			
		||||
							
								
								
									
										2
									
								
								services/registration/community_balance
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								services/registration/community_balance
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
Your community balance is: 0.00SRF
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								services/registration/community_balance.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								services/registration/community_balance.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
LOAD reset_incorrect 0
 | 
			
		||||
CATCH incorrect_pin flag_incorrect_pin 1
 | 
			
		||||
CATCH pin_entry flag_account_authorized 0
 | 
			
		||||
LOAD quit_with_balance 0
 | 
			
		||||
HALT
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/community_balance_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/community_balance_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Community  balance
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/community_balance_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/community_balance_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Salio la kikundi
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/confirm_create_pin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/confirm_create_pin
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Enter your four number PIN again:
 | 
			
		||||
							
								
								
									
										4
									
								
								services/registration/confirm_create_pin.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								services/registration/confirm_create_pin.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
LOAD save_pin 0
 | 
			
		||||
HALT
 | 
			
		||||
LOAD verify_pin 8
 | 
			
		||||
INCMP account_creation *
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/confirm_create_pin_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/confirm_create_pin_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka PIN yako tena:
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/confirm_pin_change
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/confirm_pin_change
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Confirm your new PIN:
 | 
			
		||||
							
								
								
									
										7
									
								
								services/registration/confirm_pin_change.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								services/registration/confirm_pin_change.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
LOAD verify_pin 0
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
RELOAD verify_pin
 | 
			
		||||
CATCH  create_pin_mismatch flag_pin_mismatch 1
 | 
			
		||||
MOVE pin_reset_success
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/create_pin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/create_pin
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Please enter a new four number PIN for your account:
 | 
			
		||||
							
								
								
									
										9
									
								
								services/registration/create_pin.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								services/registration/create_pin.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
LOAD create_account 0
 | 
			
		||||
CATCH account_creation_failed flag_account_creation_failed 1
 | 
			
		||||
MOUT exit 0
 | 
			
		||||
HALT
 | 
			
		||||
LOAD save_pin 0
 | 
			
		||||
RELOAD save_pin
 | 
			
		||||
CATCH . flag_incorrect_pin 1
 | 
			
		||||
INCMP quit 0
 | 
			
		||||
INCMP confirm_create_pin *
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/create_pin_mismatch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/create_pin_mismatch
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
The PIN is not a match. Try again
 | 
			
		||||
							
								
								
									
										5
									
								
								services/registration/create_pin_mismatch.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								services/registration/create_pin_mismatch.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
MOUT retry 1
 | 
			
		||||
MOUT quit 9
 | 
			
		||||
HALT
 | 
			
		||||
INCMP confirm_create_pin 1
 | 
			
		||||
INCMP quit 9
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/create_pin_mismatch_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/create_pin_mismatch_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
PIN uliyoweka haifanani. Jaribu tena
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/create_pin_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/create_pin_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Tafadhali weka PIN mpya yenye nambari nne kwa akaunti yako:
 | 
			
		||||
							
								
								
									
										5
									
								
								services/registration/display_profile_info
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								services/registration/display_profile_info
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
Wasifu wangu
 | 
			
		||||
Name: Not provided
 | 
			
		||||
Gender: Not provided
 | 
			
		||||
Age: Not provided
 | 
			
		||||
Location: Not provided
 | 
			
		||||
							
								
								
									
										3
									
								
								services/registration/display_profile_info.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								services/registration/display_profile_info.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
							
								
								
									
										0
									
								
								services/registration/display_profile_info_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								services/registration/display_profile_info_swa
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								services/registration/edit_gender_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_gender_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Edit gender
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_gender_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_gender_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka jinsia
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_location_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_location_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Edit location
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_location_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_location_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka eneo
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_name_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_name_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Edit name
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_name_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_name_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka jina
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_offerings_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_offerings_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Edit offerings
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_offerings_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_offerings_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka unachouza
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_profile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_profile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
My profile
 | 
			
		||||
							
								
								
									
										20
									
								
								services/registration/edit_profile.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								services/registration/edit_profile.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
LOAD reset_account_authorized 16
 | 
			
		||||
LOAD reset_allow_update 0
 | 
			
		||||
RELOAD reset_allow_update
 | 
			
		||||
MOUT edit_name 1
 | 
			
		||||
MOUT edit_gender 2
 | 
			
		||||
MOUT edit_yob 3
 | 
			
		||||
MOUT edit_location 4
 | 
			
		||||
MOUT edit_offerings 5
 | 
			
		||||
MOUT view 6
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
LOAD set_reset_single_edit 0
 | 
			
		||||
RELOAD set_reset_single_edit
 | 
			
		||||
INCMP enter_name 1
 | 
			
		||||
INCMP select_gender 2
 | 
			
		||||
INCMP enter_yob 3
 | 
			
		||||
INCMP enter_location 4
 | 
			
		||||
INCMP enter_offerings 5
 | 
			
		||||
INCMP view_profile 6
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_profile_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_profile_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Wasifu wangu
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_yob_menu
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_yob_menu
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Edit year of birth
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/edit_yob_menu_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/edit_yob_menu_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka mwaka wa kuzaliwa
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/enter_familyname
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/enter_familyname
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Enter family name:
 | 
			
		||||
							
								
								
									
										5
									
								
								services/registration/enter_familyname.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								services/registration/enter_familyname.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
LOAD save_firstname 0
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
INCMP select_gender *
 | 
			
		||||
							
								
								
									
										0
									
								
								services/registration/enter_familyname_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								services/registration/enter_familyname_swa
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										1
									
								
								services/registration/enter_location
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/enter_location
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Enter your location:
 | 
			
		||||
							
								
								
									
										9
									
								
								services/registration/enter_location.vis
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								services/registration/enter_location.vis
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
CATCH incorrect_date_format flag_incorrect_date_format 1
 | 
			
		||||
LOAD save_yob 0
 | 
			
		||||
CATCH update_success flag_allow_update 1
 | 
			
		||||
MOUT back 0
 | 
			
		||||
HALT
 | 
			
		||||
INCMP _ 0
 | 
			
		||||
LOAD save_location 0
 | 
			
		||||
CATCH pin_entry flag_single_edit 1
 | 
			
		||||
INCMP enter_offerings *
 | 
			
		||||
							
								
								
									
										1
									
								
								services/registration/enter_location_swa
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								services/registration/enter_location_swa
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
Weka eneo:
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user