diff --git a/cmd/main.go b/cmd/main.go index 2daee8c..3be8980 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,10 +2,14 @@ package main import ( "context" + "encoding/csv" "flag" "fmt" + "io" + "log" "os" "path" + "strconv" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/engine" @@ -13,7 +17,6 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" - "git.grassecon.net/urdt/ussd/internal/models" ) var ( @@ -35,22 +38,43 @@ func main() { ctx := context.Background() st := state.NewState(16) st.UseDebug() - state.FlagDebugger.Register(models.USERFLAG_LANGUAGE_SET, "LANGUAGE_CHANGE") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATED, "ACCOUNT_CREATED") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_SUCCESS, "ACCOUNT_SUCCESS") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_PENDING, "ACCOUNT_PENDING") - state.FlagDebugger.Register(models.USERFLAG_INCORRECTPIN, "INCORRECTPIN") - state.FlagDebugger.Register(models.USERFLAG_INCORRECTDATEFORMAT, "INVALIDDATEFORMAT") - state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT, "INVALIDRECIPIENT") - state.FlagDebugger.Register(models.USERFLAG_PINMISMATCH, "PINMISMATCH") - state.FlagDebugger.Register(models.USERFLAG_PIN_SET, "PIN_SET") - state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE, "INVALIDRECIPIENT_WITH_INVITE") - state.FlagDebugger.Register(models.USERFLAG_INVALID_AMOUNT, "INVALIDAMOUNT") - state.FlagDebugger.Register(models.USERFLAG_ALLOW_UPDATE, "UNLOCKFORUPDATE") - state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "VALIDPIN") - state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "ACCOUNTUNLOCKED") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATION_FAILED, "ACCOUNT_CREATION_FAILED") - state.FlagDebugger.Register(models.USERFLAG_SINGLE_EDIT, "SINGLEEDIT") + + pfp := path.Join(scriptDir, "pp.csv") + file, err := os.Open(pfp) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to open CSV file: %v\n", err) + os.Exit(1) + } + defer file.Close() + reader := csv.NewReader(file) + + // Iterate through the CSV records and register the flags + for { + record, err := reader.Read() + if err != nil { + if err == io.EOF { + break + } + fmt.Fprintf(os.Stderr, "Error reading CSV file: %v\n", err) + os.Exit(1) + } + + // Ensure the record starts with "flag" and has at least 3 columns + if len(record) < 3 || record[0] != "flag" { + continue + } + + flagName := record[1] + flagValue, err := strconv.Atoi(record[2]) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to convert flag value %s to integer: %v\n", record[2], err) + continue + } + + // Register the flag + log.Printf("Registering flagName:%s; flagValue:%v", flagName, flagValue) + state.FlagDebugger.Register(uint32(flagValue), flagName) + } rfs := resource.NewFsResource(scriptDir) ca := cache.NewCache() @@ -60,7 +84,7 @@ func main() { } dp := path.Join(scriptDir, ".state") - err := os.MkdirAll(dp, 0700) + err = os.MkdirAll(dp, 0700) if err != nil { fmt.Fprintf(os.Stderr, "state dir create exited with error: %v\n", err) os.Exit(1) @@ -83,7 +107,11 @@ func main() { fp := path.Join(dp, sessionId) - ussdHandlers := ussd.NewHandlers(fp, &st) + ussdHandlers, err := ussd.NewHandlers(fp, &st, sessionId) + + if err != nil { + fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err) + } rfs.AddLocalFunc("select_language", ussdHandlers.SetLanguage) rfs.AddLocalFunc("create_account", ussdHandlers.CreateAccount) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4eb2a83..c47f2e0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,19 +3,21 @@ package ussd import ( "bytes" "context" + "errors" "fmt" "path" "regexp" "strconv" "strings" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/lang" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/handlers/server" - "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" + "github.com/graygnuorg/go-gdbm" "gopkg.in/leonelquinteros/gotext.v1" ) @@ -24,31 +26,86 @@ var ( translationDir = path.Join(scriptDir, "locale") ) +const ( + TrackingIdKey = "TRACKINGID" + PublicKeyKey = "PUBLICKEY" + CustodialIdKey = "CUSTODIALID" + AccountPin = "ACCOUNTPIN" + AccountStatus = "ACCOUNTSTATUS" + FirstName = "FIRSTNAME" + FamilyName = "FAMILYNAME" + YearOfBirth = "YOB" + Location = "LOCATION" + Gender = "GENDER" + Offerings = "OFFERINGS" + Recipient = "RECIPIENT" + Amount = "AMOUNT" + AccountCreated = "ACCOUNTCREATED" +) + +func toBytes(s string) []byte { + return []byte(s) +} + type FSData struct { Path string St *state.State } +// FlagManager handles centralized flag management +type FlagManager struct { + parser *asm.FlagParser +} + +// NewFlagManager creates a new FlagManager instance +func NewFlagManager(csvPath string) (*FlagManager, error) { + parser := asm.NewFlagParser() + _, err := parser.Load(csvPath) + if err != nil { + return nil, fmt.Errorf("failed to load flag parser: %v", err) + } + + return &FlagManager{ + parser: parser, + }, nil +} + +// GetFlag retrieves a flag value by its label +func (fm *FlagManager) GetFlag(label string) (uint32, error) { + return fm.parser.GetFlag(label) +} type Handlers struct { fs *FSData + db *gdbm.Database + flagManager *FlagManager accountFileHandler utils.AccountFileHandlerInterface accountService server.AccountServiceInterface } -func NewHandlers(path string, st *state.State) *Handlers { +func NewHandlers(dir string, st *state.State, sessionId string) (*Handlers, error) { + filename := path.Join(scriptDir, sessionId+"_userdata.gdbm") + db, err := gdbm.Open(filename, gdbm.ModeWrcreat) + if err != nil { + panic(err) + } + pfp := path.Join(scriptDir, "pp.csv") + flagManager, err := NewFlagManager(pfp) + if err != nil { + return nil, fmt.Errorf("failed to create flag manager: %v", err) + } return &Handlers{ + db: db, fs: &FSData{ - Path: path, + Path: dir, St: st, }, - accountFileHandler: utils.NewAccountFileHandler(path + "_data"), + flagManager: flagManager, + accountFileHandler: utils.NewAccountFileHandler(dir + "_data"), accountService: &server.AccountService{}, - } + }, nil } - - // Define the regex pattern as a constant const pinPattern = `^\d{4}$` @@ -60,8 +117,9 @@ func isValidPIN(pin string) bool { // SetLanguage sets the language across the menu func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { - inputStr := string(input) res := resource.Result{} + + inputStr := string(input) switch inputStr { case "0": res.FlagSet = []uint32{state.FLAG_LANG} @@ -72,7 +130,11 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r default: } - res.FlagSet = append(res.FlagSet, models.USERFLAG_LANGUAGE_SET) + languageSetFlag, err := h.flagManager.GetFlag("flag_language_set") + if err != nil { + return res, err + } + res.FlagSet = append(res.FlagSet, languageSetFlag) return res, nil } @@ -83,68 +145,60 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - err := h.accountFileHandler.EnsureFileExists() + _, err := h.db.Fetch([]byte(AccountCreated)) if err != nil { - return res, err - } + if errors.Is(err, gdbm.ErrItemNotFound) { + accountResp, err := h.accountService.CreateAccount() + if err != nil { + flag_account_creation_failed, _ := h.flagManager.GetFlag("flag_account_creation_failed") + res.FlagSet = append(res.FlagSet, flag_account_creation_failed) + return res, err + } + data := map[string]string{ + TrackingIdKey: accountResp.Result.TrackingId, + PublicKeyKey: accountResp.Result.PublicKey, + CustodialIdKey: accountResp.Result.CustodialId.String(), + } - // if an account exists, return to prevent duplicate account creation - existingAccountData, err := h.accountFileHandler.ReadAccountData() - if existingAccountData != nil { - return res, err + for key, value := range data { + err := h.db.Store(toBytes(key), toBytes(value), true) + if err != nil { + return res, err + } + } + key := []byte(AccountCreated) + value := []byte("1") + h.db.Store(key, value, true) + flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") + res.FlagSet = append(res.FlagSet, flag_account_created) + return res, err + } else { + return res, err + } + } else { + return res, nil } - - accountResp, err := h.accountService.CreateAccount() - if err != nil { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATION_FAILED) - return res, err - } - - accountData := map[string]string{ - "TrackingId": accountResp.Result.TrackingId, - "PublicKey": accountResp.Result.PublicKey, - "CustodialId": accountResp.Result.CustodialId.String(), - "Status": "PENDING", - "Gender": "Not provided", - "YOB": "Not provided", - "Location": "Not provided", - "Offerings": "Not provided", - "FirstName": "Not provided", - "FamilyName": "Not provided", - } - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } - - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATED) - return res, err } // SavePin persists the user's PIN choice into the filesystem func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") + accountPIN := string(input) - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTPIN) + res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) - accountData["AccountPIN"] = accountPIN + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(AccountPin) + value := []byte(accountPIN) + h.db.Store(key, value, true) return res, nil } @@ -152,18 +206,22 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} menuOption := string(input) + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit") + switch menuOption { case "2": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) case "3": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) case "4": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) default: - res.FlagReset = append(res.FlagReset, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagReset, flag_single_edit) } return res, nil @@ -175,23 +233,26 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") + flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set") + + AccountPin, err := h.db.Fetch([]byte(AccountPin)) if err != nil { return res, err } - - if bytes.Equal(input, []byte(accountData["AccountPIN"])) { - res.FlagSet = []uint32{models.USERFLAG_VALIDPIN} - res.FlagReset = []uint32{models.USERFLAG_PINMISMATCH} - res.FlagSet = append(res.FlagSet, models.USERFLAG_PIN_SET) + if bytes.Equal(input, AccountPin) { + res.FlagSet = []uint32{flag_valid_pin} + res.FlagReset = []uint32{flag_pin_mismatch} + res.FlagSet = append(res.FlagSet, flag_pin_set) } else { - res.FlagSet = []uint32{models.USERFLAG_PINMISMATCH} + res.FlagSet = []uint32{flag_pin_mismatch} } return res, nil } -//codeFromCtx retrieves language codes from the context that can be used for handling translations +// codeFromCtx retrieves language codes from the context that can be used for handling translations func codeFromCtx(ctx context.Context) string { var code string engine.Logg.DebugCtxf(ctx, "in msg", "ctx", ctx, "val", code) @@ -205,19 +266,11 @@ func codeFromCtx(ctx context.Context) string { // SaveFirstname updates the first name in a JSON data file with the provided input. func (h *Handlers) SaveFirstname(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } if len(input) > 0 { name := string(input) - accountData["FirstName"] = name - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(FirstName) + value := []byte(name) + h.db.Store(key, value, true) } return res, nil @@ -226,19 +279,11 @@ func (h *Handlers) SaveFirstname(cxt context.Context, sym string, input []byte) // SaveFamilyname updates the family name in a JSON data file with the provided input. func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } if len(input) > 0 { secondname := string(input) - accountData["FamilyName"] = secondname - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(FamilyName) + value := []byte(secondname) + h.db.Store(key, value, true) } return res, nil @@ -247,21 +292,12 @@ func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) // SaveYOB updates the Year of Birth(YOB) in a JSON data file with the provided input. func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - yob := string(input) if len(yob) == 4 { yob := string(input) - accountData["YOB"] = yob - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(YearOfBirth) + value := []byte(yob) + h.db.Store(key, value, true) } return res, nil @@ -270,20 +306,12 @@ func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resou // SaveLocation updates the location in a JSON data file with the provided input. func (h *Handlers) SaveLocation(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - if len(input) > 0 { location := string(input) - accountData["Location"] = location + key := []byte(Location) + value := []byte(location) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) } return res, nil @@ -292,15 +320,8 @@ func (h *Handlers) SaveLocation(cxt context.Context, sym string, input []byte) ( // SaveGender updates the gender in a JSON data file with the provided input. func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - if len(input) > 0 { gender := string(input) - switch gender { case "1": gender = "Male" @@ -309,12 +330,9 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re case "3": gender = "Unspecified" } - accountData["Gender"] = gender - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(Gender) + value := []byte(gender) + h.db.Store(key, value, true) } return res, nil } @@ -322,20 +340,11 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re // SaveOfferings updates the offerings(goods and services provided by the user) in a JSON data file with the provided input. func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - if len(input) > 0 { offerings := string(input) - accountData["Offerings"] = offerings - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(Offerings) + value := []byte(offerings) + h.db.Store(key, value, true) } return res, nil } @@ -343,28 +352,31 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) // ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data. func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_ALLOW_UPDATE) + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + + res.FlagReset = append(res.FlagReset, flag_allow_update) return res, nil } // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") + + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } // CheckIdentifier retrieves the PublicKey from the JSON data file. func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - - res.Content = accountData["PublicKey"] - + res.Content = string(publicKey) return res, nil } @@ -372,27 +384,32 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte // It sets the required flags that control the flow. func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - pin := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + + storedpin, err := h.db.Fetch([]byte(AccountPin)) + if err == nil { + if len(input) == 4 { + if bytes.Equal(input, storedpin) { + if h.fs.St.MatchFlag(flag_account_authorized, false) { + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) + } else { + res.FlagSet = append(res.FlagSet, flag_allow_update) + res.FlagReset = append(res.FlagReset, flag_account_authorized) + } + } else { + res.FlagSet = append(res.FlagSet, flag_incorrect_pin) + res.FlagReset = append(res.FlagReset, flag_account_authorized) + return res, nil + } + } + } else if errors.Is(err, gdbm.ErrItemNotFound) { + return res, err + } else { return res, err - } - - if len(input) == 4 { - if pin != accountData["AccountPIN"] { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTPIN) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) - return res, nil - } - if h.fs.St.MatchFlag(models.USERFLAG_ACCOUNT_AUTHORIZED, false) { - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) - res.FlagSet = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_AUTHORIZED) - } else { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) - } } return res, nil } @@ -400,7 +417,10 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res // ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt. func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) + + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") + + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) return res, nil } @@ -409,33 +429,40 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") + flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") + + trackingId, err := h.db.Fetch([]byte(TrackingIdKey)) + if err != nil { return res, err } - status, err := h.accountService.CheckAccountStatus(accountData["TrackingId"]) + status, err := h.accountService.CheckAccountStatus(string(trackingId)) if err != nil { fmt.Println("Error checking account status:", err) + return res, err + + } + + err = h.db.Store(toBytes(AccountStatus), toBytes(status), true) + if err != nil { return res, nil } - accountData["Status"] = status + err = h.db.Store(toBytes(TrackingIdKey), toBytes(status), true) + if err != nil { + return res, nil + } if status == "SUCCESS" { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_SUCCESS) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_PENDING) + res.FlagSet = append(res.FlagSet, flag_account_success) + res.FlagReset = append(res.FlagReset, flag_account_pending) } else { - res.FlagReset = append(res.FlagSet, models.USERFLAG_ACCOUNT_SUCCESS) - res.FlagSet = append(res.FlagReset, models.USERFLAG_ACCOUNT_PENDING) + res.FlagReset = append(res.FlagReset, flag_account_success) + res.FlagSet = append(res.FlagSet, flag_account_pending) } - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } - return res, nil } @@ -443,39 +470,47 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") + code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - + res.Content = l.Get("Thank you for using Sarafu. Goodbye!") - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } // VerifyYob verifies the length of the given input func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") + date := string(input) _, err := strconv.Atoi(date) if err != nil { // If conversion fails, input is not numeric - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) return res, nil } if len(date) == 4 { - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) } else { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) } return res, nil } -// ResetIncorrectYob resets the incorrect date format after a new attempt +// ResetIncorrectYob resets the incorrect date format flag after a new attempt func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTDATEFORMAT) + + flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") + + res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) return res, nil } @@ -483,13 +518,13 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by // the balance as the result content func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) - accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } @@ -503,26 +538,22 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by res := resource.Result{} recipient := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") if recipient != "0" { // mimic invalid number check if recipient == "000" { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_RECIPIENT) + res.FlagSet = append(res.FlagSet, flag_invalid_recipient) res.Content = recipient return res, nil } - accountData["Recipient"] = recipient + // accountData["Recipient"] = recipient + key := []byte(Recipient) + value := []byte(recipient) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) } return res, nil @@ -532,21 +563,20 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by // as well as the invalid flags func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { + + flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") + flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") + + err := h.db.Delete([]byte(Amount)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { + return res, err + } + err = h.db.Delete([]byte(Recipient)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { return res, err } - // reset the transaction - accountData["Recipient"] = "" - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } - - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_RECIPIENT, models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE) + res.FlagReset = append(res.FlagReset, flag_invalid_recipient, flag_invalid_recipient_with_invite) return res, nil } @@ -554,20 +584,15 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt // ResetTransactionAmount resets the transaction amount and invalid flag func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { + + flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") + + err := h.db.Delete([]byte(Amount)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { return res, err } - // reset the amount - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } - - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_AMOUNT) + res.FlagReset = append(res.FlagReset, flag_invalid_amount) return res, nil } @@ -576,13 +601,12 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input // the result content. func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } @@ -596,14 +620,18 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - amountStr := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() + flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") + + amountStr := string(input) + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) + if err != nil { return res, err } - balanceStr, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balanceStr, err := h.accountService.CheckBalance(string(publicKey)) + if err != nil { return res, err } @@ -623,28 +651,29 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) re := regexp.MustCompile(`^(\d+(\.\d+)?)\s*(?:CELO)?$`) matches := re.FindStringSubmatch(strings.TrimSpace(amountStr)) if len(matches) < 2 { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } inputAmount, err := strconv.ParseFloat(matches[1], 64) if err != nil { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } if inputAmount > balanceValue { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } res.Content = fmt.Sprintf("%.3f", inputAmount) // Format to 3 decimal places - accountData["Amount"] = res.Content + key := []byte(Amount) + value := []byte(res.Content) + h.db.Store(key, value, true) - err = h.accountFileHandler.WriteAccountData(accountData) if err != nil { return res, err } @@ -655,74 +684,37 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // GetRecipient returns the transaction recipient from a JSON data file. func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + recipient, err := h.db.Fetch([]byte(Recipient)) if err != nil { return res, err } - res.Content = accountData["Recipient"] + res.Content = string(recipient) return res, nil } -// GetProfileInfo retrieves and formats the profile information of a user from a JSON data file. -func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { - res := resource.Result{} - var age string - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - var name string - if accountData["FirstName"] == "Not provided" || accountData["FamilyName"] == "Not provided" { - name = "Not provided" - } else { - name = accountData["FirstName"] + " " + accountData["FamilyName"] - } - - gender := accountData["Gender"] - yob := accountData["YOB"] - location := accountData["Location"] - offerings := accountData["Offerings"] - if yob == "Not provided" { - age = "Not provided" - } else { - ageInt, err := strconv.Atoi(yob) - if err != nil { - return res, nil - } - age = strconv.Itoa(utils.CalculateAgeWithYOB(ageInt)) - } - formattedData := fmt.Sprintf("Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", name, gender, age, location, offerings) - res.Content = formattedData - return res, nil -} - -// GetSender retrieves the public key from a JSON data file. +// GetSender retrieves the public key from the Gdbm Db func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - res.Content = accountData["PublicKey"] + res.Content = string(publicKey) return res, nil } -// GetAmount retrieves the amount from a JSON data file. +// GetAmount retrieves the amount from teh Gdbm Db func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + amount, err := h.db.Fetch([]byte(Amount)) if err != nil { return res, err } - - res.Content = accountData["Amount"] + res.Content = string(amount) return res, nil } @@ -731,19 +723,22 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res // gracefully exiting the session. func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") + code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } res.Content = l.Get("Your account balance is %s", balance) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } @@ -754,26 +749,92 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - // TODO // Use the amount, recipient and sender to call the API and initialize the transaction - res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", accountData["Recipient"], accountData["Amount"], accountData["PublicKey"]) - - // reset the transaction - accountData["Recipient"] = "" - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) + if err != nil { + return res, err + } + amount, err := h.db.Fetch([]byte(Amount)) + if err != nil { + return res, err + } + recipient, err := h.db.Fetch([]byte(Recipient)) if err != nil { return res, err } - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) + + account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, account_authorized_flag) + return res, nil +} + +// GetProfileInfo retrieves and formats the profile information of a user from a Gdbm backed storage. +func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { + res := resource.Result{} + + // Define default values + defaultValue := "Not provided" + name := defaultValue + familyName := defaultValue + yob := defaultValue + gender := defaultValue + location := defaultValue + offerings := defaultValue + + // Fetch data using a map for better organization + dataKeys := map[string]*string{ + FirstName: &name, + FamilyName: &familyName, + YearOfBirth: &yob, + Location: &location, + Gender: &gender, + Offerings: &offerings, + } + + // Iterate over keys and fetch values + //iter := h.db.Iterator() + next := h.db.Iterator() + //defer iter.Close() // Ensure the iterator is closed + for key, err := next(); err == nil; key, err = next() { + if valuePointer, ok := dataKeys[string(key)]; ok { + value, fetchErr := h.db.Fetch(key) + if fetchErr == nil { + *valuePointer = string(value) + } + } + } + + // Construct the full name + if familyName != defaultValue { + if name == defaultValue { + name = familyName + } else { + name = name + " " + familyName + } + } + + // Calculate age from year of birth + var age string + if yob != defaultValue { + yobInt, err := strconv.Atoi(yob) + if err != nil { + return res, fmt.Errorf("invalid year of birth: %v", err) + } + age = strconv.Itoa(utils.CalculateAgeWithYOB(yobInt)) + } else { + age = defaultValue + } + + // Format the result + formattedData := fmt.Sprintf("Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", name, gender, age, location, offerings) + res.Content = formattedData return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 5755092..5bcf7c1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -3,6 +3,7 @@ package ussd import ( "context" "encoding/json" + "fmt" "os" "path/filepath" "testing" @@ -20,6 +21,15 @@ type MockAccountService struct { mock.Mock } +type MockFlagParser struct { + mock.Mock +} + +func (m *MockFlagParser) GetFlag(key string) (uint32, error) { + args := m.Called(key) + return args.Get(0).(uint32), args.Error(1) +} + func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { args := m.Called() return args.Get(0).(*models.AccountResponse), args.Error(1) @@ -69,11 +79,20 @@ func TestCreateAccount(t *testing.T) { // Set up expectations for the mock account service mockAccountService.On("CreateAccount").Return(mockAccountResponse, nil) + mockParser := new(MockFlagParser) + + flag_account_created := uint32(1) + flag_account_creation_failed := uint32(2) + + mockParser.On("GetFlag", "flag_account_created").Return(flag_account_created, nil) + mockParser.On("GetFlag", "flag_account_creation_failed").Return(flag_account_creation_failed, nil) + // Initialize Handlers with mock account service h := &Handlers{ fs: &FSData{Path: accountFilePath}, accountFileHandler: accountFileHandler, accountService: mockAccountService, + parser: mockParser, } tests := []struct { @@ -86,7 +105,7 @@ func TestCreateAccount(t *testing.T) { name: "New account creation", existingData: nil, expectedResult: resource.Result{ - FlagSet: []uint32{models.USERFLAG_ACCOUNT_CREATED}, + FlagSet: []uint32{flag_account_created}, }, expectedData: map[string]string{ "TrackingId": "test-tracking-id", @@ -247,10 +266,16 @@ func TestSavePin(t *testing.T) { // Create a new AccountFileHandler and set it in the Handlers struct accountFileHandler := utils.NewAccountFileHandler(accountFilePath) + mockParser := new(MockFlagParser) + h := &Handlers{ accountFileHandler: accountFileHandler, + parser: mockParser, } + flag_incorrect_pin := uint32(1) + mockParser.On("GetFlag", "flag_incorrect_pin").Return(flag_incorrect_pin, nil) + tests := []struct { name string input []byte @@ -271,21 +296,21 @@ func TestSavePin(t *testing.T) { { name: "Invalid PIN - non-numeric", input: []byte("12ab"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, { name: "Invalid PIN - less than 4 digits", input: []byte("123"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, { name: "Invalid PIN - more than 4 digits", input: []byte("12345"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, @@ -293,7 +318,6 @@ func TestSavePin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Ensure the file exists before running the test err := accountFileHandler.EnsureFileExists() if err != nil { t.Fatalf("Failed to ensure account file exists: %v", err) @@ -690,7 +714,6 @@ func TestSaveOfferings(t *testing.T) { } } - func TestSaveGender(t *testing.T) { // Create a new instance of MockAccountFileHandler mockFileHandler := new(mocks.MockAccountFileHandler) @@ -732,7 +755,7 @@ func TestSaveGender(t *testing.T) { expectedError: nil, expectedGender: "Unspecified", }, - + { name: "Empty Input", input: []byte{}, @@ -752,7 +775,7 @@ func TestSaveGender(t *testing.T) { mockFileHandler.On("WriteAccountData", mock.MatchedBy(func(data map[string]string) bool { return data["Gender"] == tt.expectedGender })).Return(tt.writeError) - } else if len(tt.input) == 0 { + } else if len(tt.input) == 0 { // For empty input, no WriteAccountData call should be made mockFileHandler.On("WriteAccountData", mock.Anything).Maybe().Return(tt.writeError) } @@ -876,3 +899,95 @@ func TestGetAmount(t *testing.T) { } } +func TestGetProfileInfo(t *testing.T) { + tests := []struct { + name string + accountData map[string]string + readError error + expectedResult resource.Result + expectedError error + }{ + { + name: "Complete Profile", + accountData: map[string]string{ + "FirstName": "John", + "FamilyName": "Doe", + "Gender": "Male", + "YOB": "1980", + "Location": "Mombasa", + "Offerings": "Product A", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n", + "John", "Doe", "Male", 44, "Mombasa", "Product A", + ), + }, + expectedError: nil, + }, + { + name: "Profile with Not Provided Fields", + accountData: map[string]string{ + "FirstName": "Not provided", + "FamilyName": "Doe", + "Gender": "Female", + "YOB": "1995", + "Location": "Not provided", + "Offerings": "Service B", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n", + "Not provided", "Female", 29, "Not provided", "Service B", + ), + }, + expectedError: nil, + }, + { + name: "Profile with YOB as Not provided", + accountData: map[string]string{ + "FirstName": "Not provided", + "FamilyName": "Doe", + "Gender": "Female", + "YOB": "Not provided", + "Location": "Not provided", + "Offerings": "Service B", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", + "Not provided", "Female", "Not provided", "Not provided", "Service B", + ), + }, + expectedError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a new instance of MockAccountFileHandler + mockFileHandler := new(mocks.MockAccountFileHandler) + + // Set up the mock expectations + mockFileHandler.On("ReadAccountData").Return(tt.accountData, tt.readError) + + // Create the Handlers instance with the mock file handler + h := &Handlers{ + accountFileHandler: mockFileHandler, + } + + // Call the method + result, err := h.GetProfileInfo(context.Background(), "get_profile_info", nil) + + // Assert the results + assert.Equal(t, tt.expectedResult, result) + assert.Equal(t, tt.expectedError, err) + + // Assert all expectations were met + mockFileHandler.AssertExpectations(t) + }) + } +} diff --git a/internal/models/flags.go b/internal/models/flags.go deleted file mode 100644 index ba6d617..0000000 --- a/internal/models/flags.go +++ /dev/null @@ -1,22 +0,0 @@ -package models - -import "git.defalsify.org/vise.git/state" - -const ( - USERFLAG_LANGUAGE_SET = iota + state.FLAG_USERSTART - USERFLAG_ACCOUNT_CREATED - USERFLAG_ACCOUNT_PENDING - USERFLAG_ACCOUNT_SUCCESS - USERFLAG_ACCOUNT_AUTHORIZED - USERFLAG_INVALID_RECIPIENT - USERFLAG_INVALID_RECIPIENT_WITH_INVITE - USERFLAG_INCORRECTPIN - USERFLAG_ALLOW_UPDATE - USERFLAG_INVALID_AMOUNT - USERFLAG_PIN_SET - USERFLAG_VALIDPIN - USERFLAG_PINMISMATCH - USERFLAG_INCORRECTDATEFORMAT - USERFLAG_ACCOUNT_CREATION_FAILED - USERFLAG_SINGLE_EDIT -) diff --git a/services/registration/Makefile b/services/registration/Makefile index 578ebc6..1863b0b 100644 --- a/services/registration/Makefile +++ b/services/registration/Makefile @@ -4,7 +4,7 @@ TXTS = $(wildcard ./*.txt.orig) # Rule to build .bin files from .vis files %.vis: - go run ../../go-vise/dev/asm $(basename $@).vis > $(basename $@).bin + go run ../../go-vise/dev/asm -f pp.csv $(basename $@).vis > $(basename $@).bin @echo "Built $(basename $@).bin from $(basename $@).vis" # Rule to copy .orig files to .txt diff --git a/services/registration/account_creation.vis b/services/registration/account_creation.vis index e3ecebb..f4f326b 100644 --- a/services/registration/account_creation.vis +++ b/services/registration/account_creation.vis @@ -1,4 +1,4 @@ RELOAD verify_pin -CATCH create_pin_mismatch 20 1 +CATCH create_pin_mismatch flag_pin_mismatch 1 LOAD quit 0 HALT diff --git a/services/registration/account_pending.vis b/services/registration/account_pending.vis index 19e308c..d122613 100644 --- a/services/registration/account_pending.vis +++ b/services/registration/account_pending.vis @@ -1,3 +1,3 @@ RELOAD check_account_status -CATCH main 11 1 +CATCH main flag_account_success 1 HALT diff --git a/services/registration/amount.vis b/services/registration/amount.vis index 884c8a5..9b2970a 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -5,7 +5,7 @@ MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount -CATCH invalid_amount 17 1 +CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 LOAD get_recipient 12 LOAD get_sender 64 diff --git a/services/registration/balances.vis b/services/registration/balances.vis index 552acdb..dd14501 100644 --- a/services/registration/balances.vis +++ b/services/registration/balances.vis @@ -1,4 +1,4 @@ -LOAD reset_unlocked 0 +LOAD reset_account_authorized 0 MOUT my_balance 1 MOUT community_balance 2 MOUT back 0 diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index 4894944..151c6d8 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index 4994863..e0e330f 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -1,9 +1,9 @@ LOAD create_account 0 -CATCH account_creation_failed 22 1 +CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT LOAD save_pin 0 RELOAD save_pin -CATCH . 15 1 +CATCH . flag_incorrect_pin 1 INCMP quit 0 INCMP confirm_create_pin * diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index f6ccf77..00bed3d 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,12 +1,9 @@ -CATCH incorrect_date_format 21 1 +CATCH incorrect_date_format flag_incorrect_date_format 1 LOAD save_yob 0 -CATCH update_success 16 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT INCMP _ 0 LOAD save_location 0 -CATCH pin_entry 23 1 +CATCH pin_entry flag_single_edit 1 INCMP enter_offerings * - - - diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis index f48f1ea..e590321 100644 --- a/services/registration/enter_offerings.vis +++ b/services/registration/enter_offerings.vis @@ -1,6 +1,6 @@ LOAD save_location 0 -CATCH incorrect_pin 15 1 -CATCH update_success 16 1 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT LOAD save_offerings 0 diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis index 3b3f0d2..3b27846 100644 --- a/services/registration/enter_yob.vis +++ b/services/registration/enter_yob.vis @@ -1,9 +1,9 @@ LOAD save_gender 0 -CATCH update_success 16 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT INCMP _ 0 LOAD verify_yob 8 LOAD save_yob 0 -CATCH pin_entry 23 1 +CATCH pin_entry flag_single_edit 1 INCMP enter_location * diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index 4894944..151c6d8 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/pp.csv b/services/registration/pp.csv new file mode 100644 index 0000000..fd552e4 --- /dev/null +++ b/services/registration/pp.csv @@ -0,0 +1,16 @@ +flag,flag_language_set,8,checks whether the user has set their prefered language +flag,flag_account_created,9,this is set when an account has been created on the API +flag,flag_account_creation_failed,10,this is set when there's an error from the API during account creation +flag,flag_account_pending,11,this is set when an account does not have a status of SUCCESS +flag,flag_account_success,12,this is set when an account has a status of SUCCESS +flag,flag_pin_mismatch,13,this is set when the confirmation PIN matches the initial PIN during registration +flag,flag_pin_set,14,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu +flag,flag_account_authorized,15,this is set to allow a user access guarded nodes after providing a correct PIN +flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid +flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform +flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid +flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,flag_valid_pin,20,this is set when the given PIN is valid +flag,flag_allow_update,21,this is set to allow a user to update their profile data +flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth +flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid diff --git a/services/registration/root.vis b/services/registration/root.vis index 14b77f8..6e3b79d 100644 --- a/services/registration/root.vis +++ b/services/registration/root.vis @@ -1,7 +1,7 @@ -CATCH select_language 8 0 -CATCH terms 9 0 +CATCH select_language flag_language_set 0 +CATCH terms flag_account_created 0 LOAD check_account_status 0 -CATCH account_pending 10 1 -CATCH create_pin 18 0 -CATCH main 11 1 +CATCH account_pending flag_account_pending 1 +CATCH create_pin flag_pin_set 0 +CATCH main flag_account_success 1 HALT diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index 3029406..dd354fc 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -1,12 +1,12 @@ LOAD save_familyname 0 -CATCH update_success 16 1 +CATCH update_success flag_allow_update 1 MOUT male 1 MOUT female 2 MOUT unspecified 3 MOUT back 0 HALT LOAD save_gender 0 -CATCH pin_entry 23 1 +CATCH pin_entry flag_single_edit 1 INCMP _ 0 INCMP enter_yob 1 INCMP enter_yob 2 diff --git a/services/registration/send.vis b/services/registration/send.vis index d0fe211..e120302 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -3,6 +3,6 @@ MOUT back 0 HALT LOAD validate_recipient 20 RELOAD validate_recipient -CATCH invalid_recipient 13 1 +CATCH invalid_recipient flag_invalid_recipient 1 INCMP _ 0 INCMP amount * diff --git a/services/registration/transaction_initiated.vis b/services/registration/transaction_initiated.vis index b1042f5..3b8b9f6 100644 --- a/services/registration/transaction_initiated.vis +++ b/services/registration/transaction_initiated.vis @@ -1,4 +1,6 @@ -LOAD reset_incorrect 0 +LOAD reset_incorrect 6 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH _ flag_account_authorized 0 LOAD get_amount 10 MAP get_amount RELOAD get_recipient diff --git a/services/registration/transaction_pin.vis b/services/registration/transaction_pin.vis index 5113b6c..cadbdcf 100644 --- a/services/registration/transaction_pin.vis +++ b/services/registration/transaction_pin.vis @@ -6,10 +6,9 @@ MAP get_sender MOUT back 0 MOUT quit 9 HALT -LOAD authorize_account 1 +LOAD authorize_account 6 RELOAD authorize_account -CATCH incorrect_pin 15 1 +CATCH incorrect_pin flag_incorrect_pin 1 INCMP _ 0 INCMP quit 9 -MOVE transaction_initiated - +INCMP transaction_initiated * diff --git a/services/registration/view_profile.vis b/services/registration/view_profile.vis index 8bf2b76..a7ffee4 100644 --- a/services/registration/view_profile.vis +++ b/services/registration/view_profile.vis @@ -1,8 +1,8 @@ LOAD get_profile_info 0 MAP get_profile_info -LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +LOAD reset_incorrect 6 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 MOUT back 0 HALT INCMP _ 0