From f36847d9668b92ead9267fd8d20321eba9ab0046 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:51:17 +0300 Subject: [PATCH 001/332] Added a placeholder function to get transactions --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 708139b..c291268 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -94,6 +94,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances) + ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactions) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 36d1ad5..ec27fea 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1045,3 +1045,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) return res, nil } + +// GetTransactions retrieves the transactions from the API using the "PublicKey" +func (h *Handlers) GetTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + + res.Content = "Transaction list" + + return res, nil +} \ No newline at end of file From d41ba79ae4f04c527565e3ba2dd3fbcfbbf01247 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:51:59 +0300 Subject: [PATCH 002/332] Adde the check_statement node --- services/registration/check_statement | 1 + services/registration/check_statement.vis | 9 +++++++++ services/registration/check_statement_swa | 1 + services/registration/my_account.vis | 1 + 4 files changed, 12 insertions(+) create mode 100644 services/registration/check_statement create mode 100644 services/registration/check_statement.vis create mode 100644 services/registration/check_statement_swa diff --git a/services/registration/check_statement b/services/registration/check_statement new file mode 100644 index 0000000..0e989db --- /dev/null +++ b/services/registration/check_statement @@ -0,0 +1 @@ +Please enter your PIN to view statement: \ No newline at end of file diff --git a/services/registration/check_statement.vis b/services/registration/check_statement.vis new file mode 100644 index 0000000..596dcab --- /dev/null +++ b/services/registration/check_statement.vis @@ -0,0 +1,9 @@ +LOAD authorize_account 6 +MOUT back 0 +MOUT quit 9 +HALT +RELOAD authorize_account +CATCH incorrect_pin flag_incorrect_pin 1 +INCMP _ 0 +INCMP quit 9 +INCMP transactions * diff --git a/services/registration/check_statement_swa b/services/registration/check_statement_swa new file mode 100644 index 0000000..468364f --- /dev/null +++ b/services/registration/check_statement_swa @@ -0,0 +1 @@ +Tafadhali weka PIN yako kuona taarifa ya matumizi: \ No newline at end of file diff --git a/services/registration/my_account.vis b/services/registration/my_account.vis index 43ee6a2..2b6289e 100644 --- a/services/registration/my_account.vis +++ b/services/registration/my_account.vis @@ -11,5 +11,6 @@ INCMP main 0 INCMP edit_profile 1 INCMP change_language 2 INCMP balances 3 +INCMP check_statement 4 INCMP pin_management 5 INCMP address 6 From fc0043e3f6a8b2945abd3899fce035ec233fd91e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 23 Oct 2024 14:52:15 +0300 Subject: [PATCH 003/332] Adde the transactions node --- services/registration/transactions | 1 + services/registration/transactions.vis | 11 +++++++++++ services/registration/transactions_swa | 1 + 3 files changed, 13 insertions(+) create mode 100644 services/registration/transactions create mode 100644 services/registration/transactions.vis create mode 100644 services/registration/transactions_swa diff --git a/services/registration/transactions b/services/registration/transactions new file mode 100644 index 0000000..8152c42 --- /dev/null +++ b/services/registration/transactions @@ -0,0 +1 @@ +{{.get_transactions}} \ No newline at end of file diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis new file mode 100644 index 0000000..2317ff0 --- /dev/null +++ b/services/registration/transactions.vis @@ -0,0 +1,11 @@ +LOAD get_transactions 0 +MAP get_transactions +MOUT back 0 +MOUT quit 99 +MNEXT next 11 +MPREV prev 22 +HALT +INCMP _ 0 +INCMP quit 99 +INCMP > 11 +INCMP < 22 diff --git a/services/registration/transactions_swa b/services/registration/transactions_swa new file mode 100644 index 0000000..8152c42 --- /dev/null +++ b/services/registration/transactions_swa @@ -0,0 +1 @@ +{{.get_transactions}} \ No newline at end of file From 72d5c186dd7a04b738262263ed5c785103f5e88f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:18:40 +0300 Subject: [PATCH 004/332] add admin privilege flag --- services/registration/pp.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index ec0d8c1..cee8d13 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -17,3 +17,5 @@ flag,flag_incorrect_date_format,23,this is set when the given year of birth is i flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid flag,flag_api_call_error,25,this is set when communication to an external service fails flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher +flag,flag_admin_privilege,27,this is set when a user has admin privileges. + From 0cc0bdf9f71836301879eb068f22c96382f0bf96 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:19:35 +0300 Subject: [PATCH 005/332] add pin reset for others nodes --- services/registration/enter_others_new_pin | 1 + services/registration/enter_others_new_pin.vis | 8 ++++++++ services/registration/invalid_others_pin | 1 + services/registration/invalid_others_pin.vis | 5 +++++ services/registration/no_admin_privilege | 1 + services/registration/no_admin_privilege.vis | 5 +++++ services/registration/others_pin_mismatch | 1 + services/registration/others_pin_mismatch.vis | 5 +++++ services/registration/pin_management.vis | 3 +-- services/registration/pin_reset_result | 1 + services/registration/pin_reset_result.vis | 7 +++++++ 11 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 services/registration/enter_others_new_pin create mode 100644 services/registration/enter_others_new_pin.vis create mode 100644 services/registration/invalid_others_pin create mode 100644 services/registration/invalid_others_pin.vis create mode 100644 services/registration/no_admin_privilege create mode 100644 services/registration/no_admin_privilege.vis create mode 100644 services/registration/others_pin_mismatch create mode 100644 services/registration/others_pin_mismatch.vis create mode 100644 services/registration/pin_reset_result create mode 100644 services/registration/pin_reset_result.vis diff --git a/services/registration/enter_others_new_pin b/services/registration/enter_others_new_pin new file mode 100644 index 0000000..52ae664 --- /dev/null +++ b/services/registration/enter_others_new_pin @@ -0,0 +1 @@ +Please enter new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis new file mode 100644 index 0000000..aa2818d --- /dev/null +++ b/services/registration/enter_others_new_pin.vis @@ -0,0 +1,8 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +MOUT back 0 +HALT +INCMP _ 0 +LOAD verify_new_pin 0 +RELOAD verify_new_pin +INCMP * confirm_others_new_pin diff --git a/services/registration/invalid_others_pin b/services/registration/invalid_others_pin new file mode 100644 index 0000000..acdf45f --- /dev/null +++ b/services/registration/invalid_others_pin @@ -0,0 +1 @@ +The PIN you have entered is invalid.Please try a 4 digit number instead. \ No newline at end of file diff --git a/services/registration/invalid_others_pin.vis b/services/registration/invalid_others_pin.vis new file mode 100644 index 0000000..d218e6d --- /dev/null +++ b/services/registration/invalid_others_pin.vis @@ -0,0 +1,5 @@ +MOUT retry 1 +MOUT quit 9 +HALT +INCMP enter_others_new_pin 1 +INCMP quit 9 diff --git a/services/registration/no_admin_privilege b/services/registration/no_admin_privilege new file mode 100644 index 0000000..27901dc --- /dev/null +++ b/services/registration/no_admin_privilege @@ -0,0 +1 @@ +You do not have privileges to perform this action diff --git a/services/registration/no_admin_privilege.vis b/services/registration/no_admin_privilege.vis new file mode 100644 index 0000000..3cf1e4c --- /dev/null +++ b/services/registration/no_admin_privilege.vis @@ -0,0 +1,5 @@ +MOUT quit 9 +MOUT back 0 +HALT +INCMP pin_management 0 +INCMP quit 9 diff --git a/services/registration/others_pin_mismatch b/services/registration/others_pin_mismatch new file mode 100644 index 0000000..deb9fe5 --- /dev/null +++ b/services/registration/others_pin_mismatch @@ -0,0 +1 @@ +The PIN you have entered is not a match diff --git a/services/registration/others_pin_mismatch.vis b/services/registration/others_pin_mismatch.vis new file mode 100644 index 0000000..37b3deb --- /dev/null +++ b/services/registration/others_pin_mismatch.vis @@ -0,0 +1,5 @@ +MOUT retry 1 +MOUT quit 9 +HALT +INCMP _ 1 +INCMP quit 9 diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index 3b33dad..b196a50 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -1,8 +1,7 @@ MOUT change_pin 1 MOUT reset_pin 2 -MOUT guard_pin 3 MOUT back 0 HALT INCMP _ 0 INCMP old_pin 1 - +INCMP enter_other_number 2 diff --git a/services/registration/pin_reset_result b/services/registration/pin_reset_result new file mode 100644 index 0000000..60554b9 --- /dev/null +++ b/services/registration/pin_reset_result @@ -0,0 +1 @@ +PIN reset request for {{.retrieve_blocked_number}} was successful \ No newline at end of file diff --git a/services/registration/pin_reset_result.vis b/services/registration/pin_reset_result.vis new file mode 100644 index 0000000..ed71200 --- /dev/null +++ b/services/registration/pin_reset_result.vis @@ -0,0 +1,7 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +MOUT back 0 +MOUT quit 9 +HALT +INCMP pin_management 0 +INCMP quit 9 From 3de46cef5e2758b228ea49bf597d2627185f3aa1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:45:08 +0300 Subject: [PATCH 006/332] setup pin reset nodes --- services/registration/confirm_others_new_pin | 1 + services/registration/confirm_others_new_pin.vis | 13 +++++++++++++ services/registration/confirm_pin_change.vis | 2 -- services/registration/enter_other_number | 1 + services/registration/enter_other_number.vis | 9 +++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 services/registration/confirm_others_new_pin create mode 100644 services/registration/confirm_others_new_pin.vis create mode 100644 services/registration/enter_other_number create mode 100644 services/registration/enter_other_number.vis diff --git a/services/registration/confirm_others_new_pin b/services/registration/confirm_others_new_pin new file mode 100644 index 0000000..46c3275 --- /dev/null +++ b/services/registration/confirm_others_new_pin @@ -0,0 +1 @@ +Please confirm new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis new file mode 100644 index 0000000..be6734f --- /dev/null +++ b/services/registration/confirm_others_new_pin.vis @@ -0,0 +1,13 @@ +LOAD retrieve_blocked_number 0 +MAP retrieve_blocked_number +CATCH invalid_others_pin flag_valid_pin 0 +LOAD save_temporary_pin 6 +RELOAD save_temporary_pin +CATCH pin_reset_result flag_account_authorized 1 +MOUT back 0 +HALT +INCMP _ 0 +LOAD check_pin_mismatch 0 +RELOAD check_pin_mismatch +CATCH others_pin_mismatch flag_pin_mismatch 1 +INCMP pin_entry * diff --git a/services/registration/confirm_pin_change.vis b/services/registration/confirm_pin_change.vis index 7691e01..cf485a1 100644 --- a/services/registration/confirm_pin_change.vis +++ b/services/registration/confirm_pin_change.vis @@ -3,5 +3,3 @@ MOUT back 0 HALT INCMP _ 0 INCMP * pin_reset_success - - diff --git a/services/registration/enter_other_number b/services/registration/enter_other_number new file mode 100644 index 0000000..1c4a481 --- /dev/null +++ b/services/registration/enter_other_number @@ -0,0 +1 @@ +Enter other's phone number: \ No newline at end of file diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis new file mode 100644 index 0000000..41c711a --- /dev/null +++ b/services/registration/enter_other_number.vis @@ -0,0 +1,9 @@ +CATCH no_admin_privilege flag_admin_privilege 0 +LOAD reset_account_authorized 6 +RELOAD reset_account_authorized +MOUT back 0 +HALT +INCMP _ 0 +LOAD validate_blocked_number 0 +RELOAD validate_blocked_number +INCMP enter_others_new_pin * From e4c2f644f378ffc15429a2614aebdcb12ce142f4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:47:56 +0300 Subject: [PATCH 007/332] define a key to hold number during pin reset --- internal/utils/db.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/utils/db.go b/internal/utils/db.go index 45e7681..57f3289 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -28,6 +28,7 @@ const ( DATA_ACTIVE_SYM DATA_TEMPORARY_BAL DATA_ACTIVE_BAL + DATA_BLOCKED_NUMBER ) func typToBytes(typ DataTyp) []byte { From 26073c8000c030cb6d03766bac6dffe851aca88f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 15:49:20 +0300 Subject: [PATCH 008/332] define handler functions required to reset others pin --- internal/handlers/handlerservice.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 311e694..a30f1b2 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -54,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { } func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, accountService) if err != nil { return nil, err } @@ -98,6 +98,10 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) + ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin) + ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) + ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) + ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) return ussdHandlers, nil } From aec0abb2b632af7cda2296a8eb8bcb8b5e6aaac6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 16:34:33 +0300 Subject: [PATCH 009/332] setup an admin flag --- cmd/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/main.go b/cmd/main.go index 21ca0c3..e79176c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -33,10 +33,12 @@ func main() { var size uint var sessionId string var database string + var isAdmin bool var engineDebug bool flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") + flag.BoolVar(&isAdmin, "admin", false, "if user has admin previleges") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() @@ -46,6 +48,7 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "Admin", isAdmin) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ From b97965193bb53185221a77d846e3d40e6d7ca005 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 28 Oct 2024 16:37:23 +0300 Subject: [PATCH 010/332] add pin reset for others handling --- internal/handlers/ussd/menuhandler.go | 118 ++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 9 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f4d279b..ae50724 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -27,11 +27,12 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("ussdmenuhandler") - scriptDir = path.Join("services", "registration") - translationDir = path.Join(scriptDir, "locale") - okResponse *api.OKResponse - errResponse *api.ErrResponse + logg = logging.NewVanilla().WithDomain("ussdmenuhandler") + scriptDir = path.Join("services", "registration") + translationDir = path.Join(scriptDir, "locale") + PINChangePrivilege byte = 1 + okResponse *api.OKResponse + errResponse *api.ErrResponse ) // FlagManager handles centralized flag management @@ -98,20 +99,41 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { return h } +func setAdminPrevilege(ctx context.Context, store utils.DataStore) error { + var err error + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return fmt.Errorf("missing session") + } + prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) + err = prefixdb.Put(ctx, []byte(sessionId), []byte("1")) + if err != nil { + return err + } + return nil +} + func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { var r resource.Result - if h.pe == nil { logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca) return r, nil } + h.st = h.pe.GetState() h.ca = h.pe.GetMemory() + if h.st == nil || h.ca == nil { logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) return r, fmt.Errorf("cannot get state and memory for handler") } h.pe = nil + store := h.userdataStore + err := setAdminPrevilege(ctx, store) + if err != nil { + return r, fmt.Errorf("failed to set previlege level") + } logg.DebugCtxf(ctx, "handler has been initialized", "state", h.st, "cache", h.ca) @@ -189,6 +211,26 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, nil } +func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) { + res := resource.Result{} + flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + if err != nil { + return res, err + } + if bytes.Equal(temporaryPin, input) { + res.FlagReset = append(res.FlagReset, flag_pin_mismatch) + } else { + res.FlagSet = append(res.FlagSet, flag_pin_mismatch) + } + return res, nil +} + func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} _, ok := ctx.Value("SessionId").(string) @@ -284,7 +326,6 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte if err != nil { return res, err } - if bytes.Equal(input, temporaryPin) { res.FlagSet = []uint32{flag_valid_pin} res.FlagReset = []uint32{flag_pin_mismatch} @@ -444,6 +485,14 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt return res, nil } +// ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data. +func (h *Handlers) ResetValidPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") + res.FlagReset = append(res.FlagReset, flag_valid_pin) + return res, nil +} + // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -501,6 +550,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, nil } } else { + fmt.Println("Authorizing the account else") return res, nil } return res, nil @@ -522,17 +572,37 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } + isAdmin, _ := ctx.Value("Admin").(bool) store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } + if isAdmin { + setAdminPrevilege(ctx, store) + } + prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) + accessLevel, err := prefixdb.Get(ctx, []byte(sessionId)) + if err != nil { + if !db.IsNotFound(err) { + return res, nil + } + } + isPrevileged := bytes.Equal(accessLevel, []byte("1")) + + if isPrevileged { + //Set Admin privilege Flag + res.FlagSet = append(res.FlagSet, flag_admin_privilege) + } + okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) + if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err @@ -588,7 +658,6 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res var err error flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") - date := string(input) _, err = strconv.Atoi(date) if err != nil { @@ -694,6 +763,22 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + store := h.userdataStore + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + blockedNumber := string(input) + err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) + if err != nil { + return res, nil + } + return res, nil +} + // ValidateRecipient validates that the given input is a valid phone number. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -865,6 +950,22 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } +// RetrieveBlockedNumber gets the current number during the pin reset for other's is in progress. +func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + + res.Content = string(blockedNumber) + + return res, nil +} + // GetSender returns the sessionId (phoneNumber) func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1102,7 +1203,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) if err != nil { return res, nil } - err = prefixdb.Put(ctx, []byte("bal"), []byte(voucherBalanceList)) if err != nil { return res, nil From e338ce00257316f6a9d107d81bfde9043bdea0e7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:14:49 +0300 Subject: [PATCH 011/332] load and reload only after input --- services/registration/enter_familyname.vis | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/registration/enter_familyname.vis b/services/registration/enter_familyname.vis index b9fe7b0..5a684ed 100644 --- a/services/registration/enter_familyname.vis +++ b/services/registration/enter_familyname.vis @@ -1,9 +1,8 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 -LOAD save_familyname 0 -RELOAD save_familyname MOUT back 0 HALT +LOAD save_familyname 0 RELOAD save_familyname INCMP _ 0 INCMP pin_entry * From ca13d9155c000a615a89fb6d0fce44765e67c12d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:15:22 +0300 Subject: [PATCH 012/332] replace _ with explicit back node --- services/registration/pin_management.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index b196a50..c0adc5c 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -2,6 +2,6 @@ MOUT change_pin 1 MOUT reset_pin 2 MOUT back 0 HALT -INCMP _ 0 +INCMP my_account 0 INCMP old_pin 1 INCMP enter_other_number 2 From c9bb93ede6c0deaba60868c4eb2f39a717371f59 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:15:41 +0300 Subject: [PATCH 013/332] create a simple admin store for phone numbers --- internal/utils/adminstore.go | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 internal/utils/adminstore.go diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go new file mode 100644 index 0000000..98241a6 --- /dev/null +++ b/internal/utils/adminstore.go @@ -0,0 +1,68 @@ +package utils + +import ( + "bufio" + "os" + "strconv" +) + +type AdminStore struct { + filePath string +} + +// Creates a new Admin store +func NewAdminStore(filePath string) *AdminStore { + return &AdminStore{filePath: filePath} +} + +// Seed saves a list of phonumbers with admin privileges +func (as *AdminStore) Seed() error { + adminNumbers := []int64{254705136690, 123456789012, 987654321098} + file, err := os.Create(as.filePath) + if err != nil { + return err + } + defer file.Close() + + writer := bufio.NewWriter(file) + for _, num := range adminNumbers { + _, err := writer.WriteString(strconv.FormatInt(num, 10) + "\n") + if err != nil { + return err + } + } + return writer.Flush() +} + + +func (as *AdminStore) load() ([]int64, error) { + file, err := os.Open(as.filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var numbers []int64 + scanner := bufio.NewScanner(file) + for scanner.Scan() { + num, err := strconv.ParseInt(scanner.Text(), 10, 64) + if err != nil { + return nil, err + } + numbers = append(numbers, num) + } + return numbers, scanner.Err() +} + +func (as *AdminStore) IsAdmin(phoneNumber int64) (bool, error) { + phoneNumbers, err := as.load() + if err != nil { + return false, err + } + for _, phonenumber := range phoneNumbers { + if phonenumber == phoneNumber { + return true, nil + } + } + return false, nil +} From 41da099933a9ee36605258179c305bc5965720d8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 13:24:13 +0300 Subject: [PATCH 014/332] remove the admin flag,setup an admin store --- cmd/main.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index e79176c..4d23011 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,6 +15,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/internal/utils" ) var ( @@ -33,12 +34,10 @@ func main() { var size uint var sessionId string var database string - var isAdmin bool var engineDebug bool flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") - flag.BoolVar(&isAdmin, "admin", false, "if user has admin previleges") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") flag.Parse() @@ -48,9 +47,11 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "Admin", isAdmin) pfp := path.Join(scriptDir, "pp.csv") + as := utils.NewAdminStore("admin_numbers.txt") + as.Seed() + cfg := engine.Config{ Root: "root", SessionId: sessionId, From d83962c0ba7481327d529f612df96288f09327ff Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:26:24 +0300 Subject: [PATCH 015/332] load admin numbers defined in the .env --- internal/utils/adminstore.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index 98241a6..ddd6bd9 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -2,8 +2,12 @@ package utils import ( "bufio" + "log" "os" "strconv" + "strings" + + "git.grassecon.net/urdt/ussd/initializers" ) type AdminStore struct { @@ -15,9 +19,18 @@ func NewAdminStore(filePath string) *AdminStore { return &AdminStore{filePath: filePath} } -// Seed saves a list of phonumbers with admin privileges +// Seed initializes a list of phonenumbers with admin privileges func (as *AdminStore) Seed() error { - adminNumbers := []int64{254705136690, 123456789012, 987654321098} + var adminNumbers []int64 + + numbersEnv := initializers.GetEnv("ADMIN_NUMBERS", "") + for _, numStr := range strings.Split(numbersEnv, ",") { + if num, err := strconv.ParseInt(strings.TrimSpace(numStr), 10, 64); err == nil { + adminNumbers = append(adminNumbers, num) + } else { + log.Printf("Skipping invalid number: %s", numStr) + } + } file, err := os.Create(as.filePath) if err != nil { return err @@ -34,7 +47,6 @@ func (as *AdminStore) Seed() error { return writer.Flush() } - func (as *AdminStore) load() ([]int64, error) { file, err := os.Open(as.filePath) if err != nil { From 5fd3eb3c292f5640cd556a1063e1ab5f220d3e23 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:28:58 +0300 Subject: [PATCH 016/332] set admin privilege flag --- internal/handlers/ussd/menuhandler.go | 55 ++++++++------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ae50724..d397eff 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -99,21 +99,6 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { return h } -func setAdminPrevilege(ctx context.Context, store utils.DataStore) error { - var err error - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return fmt.Errorf("missing session") - } - prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) - err = prefixdb.Put(ctx, []byte(sessionId), []byte("1")) - if err != nil { - return err - } - return nil -} - func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { var r resource.Result if h.pe == nil { @@ -124,16 +109,24 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource h.st = h.pe.GetState() h.ca = h.pe.GetMemory() + sessionId, _ := ctx.Value("SessionId").(string) + flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") + + number, _ := strconv.ParseInt(sessionId, 10, 64) + as := utils.NewAdminStore("admin_numbers.txt") + isAdmin, _ := as.IsAdmin(number) + + if isAdmin { + r.FlagSet = append(r.FlagSet, flag_admin_privilege) + } else { + r.FlagReset = append(r.FlagReset, flag_admin_privilege) + } + if h.st == nil || h.ca == nil { logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) return r, fmt.Errorf("cannot get state and memory for handler") } h.pe = nil - store := h.userdataStore - err := setAdminPrevilege(ctx, store) - if err != nil { - return r, fmt.Errorf("failed to set previlege level") - } logg.DebugCtxf(ctx, "handler has been initialized", "state", h.st, "cache", h.ca) @@ -403,6 +396,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if !ok { return res, fmt.Errorf("missing session") } + if len(input) == 4 { yob := string(input) store := h.userdataStore @@ -550,7 +544,6 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, nil } } else { - fmt.Println("Authorizing the account else") return res, nil } return res, nil @@ -572,37 +565,19 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } - isAdmin, _ := ctx.Value("Admin").(bool) + store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) if err != nil { return res, err } - if isAdmin { - setAdminPrevilege(ctx, store) - } - prefixdb := storage.NewSubPrefixDb(store, []byte("acl")) - accessLevel, err := prefixdb.Get(ctx, []byte(sessionId)) - if err != nil { - if !db.IsNotFound(err) { - return res, nil - } - } - isPrevileged := bytes.Equal(accessLevel, []byte("1")) - - if isPrevileged { - //Set Admin privilege Flag - res.FlagSet = append(res.FlagSet, flag_admin_privilege) - } okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) - if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err From 124049c92453ff6db90f91b2fe0fce4554a2061a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 14:32:17 +0300 Subject: [PATCH 017/332] add admin number defination in env --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index ab370a7..4d86f8b 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,7 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ TRACK_URL=http://localhost:5003/api/v2/account/status + + +#numbers with privileges to reset others pin +ADMIN_NUMBERS=254051722XXX,255012221XXX \ No newline at end of file From e96c8743004550e0738494b9cd3d0899d7be7885 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 15:02:40 +0300 Subject: [PATCH 018/332] repeat same node on invalid option input --- services/registration/pin_management.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/pin_management.vis b/services/registration/pin_management.vis index c0adc5c..5eb7d5a 100644 --- a/services/registration/pin_management.vis +++ b/services/registration/pin_management.vis @@ -5,3 +5,4 @@ HALT INCMP my_account 0 INCMP old_pin 1 INCMP enter_other_number 2 +INCMP . * From 449f90c95b6a4f41bd2d8c1501a99ae39d052fc7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:17:03 +0300 Subject: [PATCH 019/332] add flag to catch unregistred numbers --- services/registration/pp.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index cee8d13..406cc22 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -18,4 +18,4 @@ flag,flag_incorrect_voucher,24,this is set when the selected voucher is invalid flag,flag_api_call_error,25,this is set when communication to an external service fails flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher flag,flag_admin_privilege,27,this is set when a user has admin privileges. - +flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action From 6998c30dd1614077ddcf36ac5d1fb4ab3a4ff338 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:18:01 +0300 Subject: [PATCH 020/332] add node to handle unregistered phone numbers --- services/registration/unregistered_number | 1 + services/registration/unregistered_number.vis | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 services/registration/unregistered_number create mode 100644 services/registration/unregistered_number.vis diff --git a/services/registration/unregistered_number b/services/registration/unregistered_number new file mode 100644 index 0000000..9cc33d7 --- /dev/null +++ b/services/registration/unregistered_number @@ -0,0 +1 @@ +The number you have entered is either not registered with Sarafu or is invalid. \ No newline at end of file diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis new file mode 100644 index 0000000..f7a6a9c --- /dev/null +++ b/services/registration/unregistered_number.vis @@ -0,0 +1,7 @@ +LOAD reset_unregistered_number 0 +RELOAD reset_unregistered_number +MOUT retry 1 +MOUT quit 9 +HALT +INCMP enter_other_number 1 +INCMP quit 9 From 534d75631836a50bf00ebe194b33c6d061726853 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:18:39 +0300 Subject: [PATCH 021/332] catch unregistred phone numbers --- services/registration/enter_others_new_pin.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index aa2818d..1577ff1 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,3 +1,4 @@ +CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 91b85af11a223cb66de5b6cc2af900296556b3b0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:34:34 +0300 Subject: [PATCH 022/332] add reset unregistered number --- internal/handlers/handlerservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index a30f1b2..b4cd592 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -102,6 +102,7 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) + ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) return ussdHandlers, nil } From 106983a3940fc0b2066e0486fd1b47dcd1239345 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:35:01 +0300 Subject: [PATCH 023/332] use explicit back to node --- services/registration/enter_other_number.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index 41c711a..7f413da 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -3,7 +3,7 @@ LOAD reset_account_authorized 6 RELOAD reset_account_authorized MOUT back 0 HALT -INCMP _ 0 LOAD validate_blocked_number 0 RELOAD validate_blocked_number +INCMP pin_management 0 INCMP enter_others_new_pin * From d0ad6395b5670501b543e61e2a5ef2c8c5acf2ca Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 17:35:42 +0300 Subject: [PATCH 024/332] add check for unregistered phone numbers --- internal/handlers/ussd/menuhandler.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d395a8b..715a654 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -748,15 +748,35 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") + res.FlagReset = append(res.FlagReset, flag_unregistered_number) + return res, nil +} + func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error + + flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") + store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } blockedNumber := string(input) + _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + logg.Printf(logging.LVL_INFO, "Invalid or unregistered number") + res.FlagSet = append(res.FlagSet, flag_unregistered_number) + return res, nil + } else { + return res, err + } + } err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) if err != nil { return res, nil From f37483e2f04d4fa4ead95223ff2c0acc33518ff8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:15:31 +0300 Subject: [PATCH 025/332] use _ for back navigation --- services/registration/unregistered_number.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index f7a6a9c..114113f 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -3,5 +3,5 @@ RELOAD reset_unregistered_number MOUT retry 1 MOUT quit 9 HALT -INCMP enter_other_number 1 +INCMP _ 1 INCMP quit 9 From 7597b96daec3367a8a09dff0ee24f95a616107a2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:16:34 +0300 Subject: [PATCH 026/332] remove catch for unregistered number --- services/registration/enter_others_new_pin.vis | 1 - 1 file changed, 1 deletion(-) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 1577ff1..aa2818d 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,4 +1,3 @@ -CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 5a0563df94b67fade2498e8f37eef91f82a95da4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:17:43 +0300 Subject: [PATCH 027/332] group regex,check for valid number against the regex --- internal/handlers/ussd/menuhandler.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 715a654..70510ee 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -35,6 +35,12 @@ var ( backOption = []byte("0") ) +// Define the regex patterns as constants +const ( + phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}` + pinPattern = `^\d{4}$` +) + // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser @@ -82,15 +88,17 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s return h, nil } -// Define the regex pattern as a constant -const pinPattern = `^\d{4}$` - // isValidPIN checks whether the given input is a 4 digit number func isValidPIN(pin string) bool { match, _ := regexp.MatchString(pinPattern, pin) return match } +func isValidPhoneNumber(phonenumber string) bool { + match, _ := regexp.MatchString(phoneRegex, phonenumber) + return match +} + func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { if h.pe != nil { panic("persister already set") @@ -255,7 +263,6 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt } flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - accountPIN := string(input) // Validate that the PIN is a 4-digit number @@ -768,6 +775,9 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input } blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + if !isValidPhoneNumber(blockedNumber) { + return res, nil + } if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Invalid or unregistered number") From 0a97f610a47746c25cc7748ebbf29937e3bc11a2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 29 Oct 2024 22:23:22 +0300 Subject: [PATCH 028/332] catch unregistred number --- services/registration/enter_other_number.vis | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index 7f413da..e2fa243 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -1,9 +1,10 @@ CATCH no_admin_privilege flag_admin_privilege 0 -LOAD reset_account_authorized 6 +LOAD reset_account_authorized 0 RELOAD reset_account_authorized MOUT back 0 HALT -LOAD validate_blocked_number 0 +INCMP _ 0 +LOAD validate_blocked_number 20 RELOAD validate_blocked_number -INCMP pin_management 0 +CATCH unregistered_number flag_unregistered_number 1 INCMP enter_others_new_pin * From dd764a2e24514b9799e78973c7ccb80c510f1593 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 01:28:55 +0000 Subject: [PATCH 029/332] Export db datatypes,tools --- {internal/utils => common}/db.go | 2 +- internal/handlers/ussd/menuhandler.go | 108 ++++++++++----------- internal/handlers/ussd/menuhandler_test.go | 97 +++++++++--------- internal/testutil/mocks/userdbmock.go | 6 +- internal/utils/userStore.go | 14 +-- 5 files changed, 114 insertions(+), 113 deletions(-) rename {internal/utils => common}/db.go (97%) diff --git a/internal/utils/db.go b/common/db.go similarity index 97% rename from internal/utils/db.go rename to common/db.go index 2c1e6fa..349d3aa 100644 --- a/internal/utils/db.go +++ b/common/db.go @@ -1,4 +1,4 @@ -package utils +package common import ( "encoding/binary" diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dae4236..e2bcfaf 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -151,9 +151,9 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r trackingId := okResponse.Result["trackingId"].(string) publicKey := okResponse.Result["publicKey"].(string) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: trackingId, - utils.DATA_PUBLIC_KEY: publicKey, + data := map[common.DataTyp]string{ + common.DATA_TRACKING_ID: trackingId, + common.DATA_PUBLIC_KEY: publicKey, } store := h.userdataStore for key, value := range data { @@ -166,7 +166,7 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r if err != nil { return err } - err = store.WriteEntry(ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) + err = store.WriteEntry(ctx, publicKeyNormalized, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) if err != nil { return err } @@ -186,7 +186,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, fmt.Errorf("missing session") } store := h.userdataStore - _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_CREATED) + _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") @@ -242,7 +242,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(accountPIN)) if err != nil { return res, err } @@ -259,7 +259,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -268,7 +268,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt } else { res.FlagSet = append(res.FlagSet, flag_pin_mismatch) } - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, err } @@ -290,7 +290,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -303,7 +303,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte res.FlagSet = []uint32{flag_pin_mismatch} } - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, err } @@ -335,7 +335,7 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) } firstName := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(firstName)) if err != nil { return res, err } @@ -358,7 +358,7 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) } familyName := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(familyName)) if err != nil { return res, err } @@ -380,7 +380,7 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if len(input) == 4 { yob := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob)) + err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(yob)) if err != nil { return res, err } @@ -403,7 +403,7 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( } location := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location)) + err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(location)) if err != nil { return res, err } @@ -426,7 +426,7 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re } gender := strings.Split(symbol, "_")[1] store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) + err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(gender)) if err != nil { return res, nil } @@ -445,7 +445,7 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) if len(input) > 0 { offerings := string(input) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(offerings)) if err != nil { return res, nil } @@ -481,7 +481,7 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - publicKey, _ := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) res.Content = string(publicKey) @@ -502,7 +502,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") store := h.userdataStore - AccountPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN) + AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) if err != nil { return res, err } @@ -548,7 +548,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, fmt.Errorf("missing session") } store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, err } @@ -654,7 +654,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( store := h.userdataStore // get the active sym and active balance - activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { if db.IsNotFound(err) { balance := "0.00" @@ -665,7 +665,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -687,7 +687,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input balanceType := strings.Split(symbol, "_")[0] store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, err } @@ -737,7 +737,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, nil } store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recipient)) + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) if err != nil { return res, nil } @@ -760,12 +760,12 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) if err != nil { return res, nil } - err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte("")) if err != nil { return res, nil } @@ -787,7 +787,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("")) if err != nil { return res, nil } @@ -809,7 +809,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res } store := h.userdataStore - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -834,7 +834,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) var balanceValue float64 // retrieve the active balance - activeBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL) + activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { return res, err } @@ -860,7 +860,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // Format the amount with 2 decimal places before saving formattedAmount := fmt.Sprintf("%.2f", inputAmount) - err = store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(formattedAmount)) + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) if err != nil { return res, err } @@ -878,7 +878,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, fmt.Errorf("missing session") } store := h.userdataStore - recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) res.Content = string(recipient) @@ -910,12 +910,12 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res store := h.userdataStore // retrieve the active symbol - activeSym, err := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { return res, err } - amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) + amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) res.Content = fmt.Sprintf("%s %s", string(amount), string(activeSym)) @@ -939,11 +939,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] // Use the amount, recipient and sender to call the API and initialize the transaction store := h.userdataStore - amount, _ := store.ReadEntry(ctx, sessionId, utils.DATA_AMOUNT) + amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) - recipient, _ := store.ReadEntry(ctx, sessionId, utils.DATA_RECIPIENT) + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) - activeSym, _ := store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) @@ -983,12 +983,12 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) } store := h.userdataStore // Retrieve user data as strings with fallback to defaultValue - firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME)) - familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME)) - yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_YOB)) - gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_GENDER)) - location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION)) - offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS)) + firstName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME)) + familyName := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME)) + yob := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_YOB)) + gender := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_GENDER)) + location := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_LOCATION)) + offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS)) // Construct the full name name := defaultValue @@ -1045,11 +1045,11 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher") // check if the user has an active sym - _, err = store.ReadEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM) + _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { if db.IsNotFound(err) { - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, nil } @@ -1072,12 +1072,12 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by defaultBal := firstVoucher.Balance // set the active symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(defaultSym)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) if err != nil { return res, err } // set the active balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(defaultBal)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) if err != nil { return res, err } @@ -1103,7 +1103,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) } store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY) + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { return res, nil } @@ -1207,11 +1207,11 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r // If a match is found, write the temporary sym and balance if matchedSymbol != "" && matchedBalance != "" { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte(matchedSymbol)) if err != nil { return res, err } - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte(matchedBalance)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte(matchedBalance)) if err != nil { return res, err } @@ -1269,34 +1269,34 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re } // get the current temporary symbol - temporarySym, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM) + temporarySym, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM) if err != nil { return res, err } // get the current temporary balance - temporaryBal, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL) + temporaryBal, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL) if err != nil { return res, err } // set the active symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(temporarySym)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(temporarySym)) if err != nil { return res, err } // set the active balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(temporaryBal)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(temporaryBal)) if err != nil { return res, err } // reset the temporary symbol - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte("")) if err != nil { return res, err } // reset the temporary balance - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte("")) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 0b70e80..b83fa61 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,7 +19,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" - "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" @@ -119,12 +118,12 @@ func TestCreateAccount(t *testing.T) { } publicKey := tt.serverResponse.Result["publicKey"].(string) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), - utils.DATA_PUBLIC_KEY: publicKey, + data := map[common.DataTyp]string{ + common.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), + common.DATA_PUBLIC_KEY: publicKey, } - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) mockCreateAccountService.On("CreateAccount").Return(tt.serverResponse, nil) for key, value := range data { @@ -135,7 +134,7 @@ func TestCreateAccount(t *testing.T) { t.Fatal(err) } - mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil) + mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil) // Call the method you want to test res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) @@ -181,7 +180,7 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FIRST_NAME, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -209,7 +208,7 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -269,7 +268,7 @@ func TestSaveTemporaryPin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -295,7 +294,7 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_YOB, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_YOB, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -323,7 +322,7 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_LOCATION, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_LOCATION, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -351,7 +350,7 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_OFFERINGS, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -413,10 +412,10 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { - expectedKey := utils.DATA_GENDER + expectedKey := common.DATA_GENDER mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -433,9 +432,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) } }) } @@ -469,7 +468,7 @@ func TestCheckIdentifier(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) + mockStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) // Create the Handlers instance with the mock store h := &Handlers{ @@ -519,8 +518,8 @@ func TestGetAmount(t *testing.T) { activeSym := "SRF" // Set up the expected behavior of the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(activeSym), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return([]byte(amount), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACTIVE_SYM).Return([]byte(activeSym), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_AMOUNT).Return([]byte(amount), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -546,7 +545,7 @@ func TestGetRecipient(t *testing.T) { recepient := "0xcasgatweksalw1018221" // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return([]byte(recepient), nil) + mockStore.On("ReadEntry", ctx, sessionId, common.DATA_RECIPIENT).Return([]byte(recepient), nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -828,7 +827,7 @@ func TestAuthorize(t *testing.T) { // Define session ID and mock data sessionId := "session123" - typ := utils.DATA_ACCOUNT_PIN + typ := common.DATA_ACCOUNT_PIN h := &Handlers{ userdataStore: mockDataStore, @@ -1011,7 +1010,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_PIN + typ := common.DATA_TEMPORARY_PIN for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1020,7 +1019,7 @@ func TestVerifyCreatePin(t *testing.T) { mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte(firstSetPin), nil) // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(firstSetPin)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(firstSetPin)).Return(nil) // Call the method under test res, err := h.VerifyCreatePin(ctx, "verify_create_pin", []byte(tt.input)) @@ -1141,11 +1140,11 @@ func TestCheckAccountStatus(t *testing.T) { status := tt.response.Result.Transaction.Status // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() // Call the method under test res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) @@ -1201,8 +1200,8 @@ func TestTransactionReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_RECIPIENT, []byte("")).Return(nil) // Call the method under test res, _ := h.TransactionReset(ctx, "transaction_reset", tt.input) @@ -1257,7 +1256,7 @@ func TestResetInvalidAmount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, []byte("")).Return(nil) // Call the method under test res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", tt.input) @@ -1320,9 +1319,9 @@ func TestInitiateTransaction(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(tt.ActiveSym, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_AMOUNT).Return(tt.Amount, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_RECIPIENT).Return(tt.Recipient, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACTIVE_SYM).Return(tt.ActiveSym, nil) // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1502,10 +1501,10 @@ func TestValidateAmount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Mock behavior for active balance retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) // Mock behavior for storing the amount (if valid) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_AMOUNT, tt.input).Return(nil).Maybe() // Call the method under test res, _ := h.ValidateAmount(ctx, "test_validate_amount", tt.input) @@ -1559,7 +1558,7 @@ func TestValidateRecipient(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, tt.input).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_RECIPIENT, tt.input).Return(nil) // Create the Handlers instance with the mock flag manager h := &Handlers{ @@ -1613,8 +1612,8 @@ func TestCheckBalance(t *testing.T) { } // Mock for user with active sym - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, common.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) + mockDataStore.On("ReadEntry", ctx, tt.sessionId, common.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) res, err := h.CheckBalance(ctx, "check_balance", []byte("")) @@ -1648,13 +1647,13 @@ func TestGetProfile(t *testing.T) { tests := []struct { name string languageCode string - keys []utils.DataTyp + keys []common.DataTyp profileInfo []string result resource.Result }{ { name: "Test with full profile information in eng", - keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "eng", result: resource.Result{ @@ -1666,7 +1665,7 @@ func TestGetProfile(t *testing.T) { }, { name: "Test with with profile information in swa ", - keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "swa", result: resource.Result{ @@ -1678,7 +1677,7 @@ func TestGetProfile(t *testing.T) { }, { name: "Test with with profile information with language that is not yet supported", - keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, + keys: []common.DataTyp{common.DATA_FAMILY_NAME, common.DATA_FIRST_NAME, common.DATA_GENDER, common.DATA_OFFERINGS, common.DATA_LOCATION, common.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "nor", result: resource.Result{ @@ -1802,9 +1801,9 @@ func TestConfirmPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -1829,12 +1828,12 @@ func TestSetVoucher(t *testing.T) { temporaryBal := []byte("tempBal") // Set expectations for the mock data store - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return(temporarySym, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, temporarySym).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, temporaryBal).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL, []byte("")).Return(nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_SYM).Return(temporarySym, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_BAL).Return(temporaryBal, nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_SYM, temporarySym).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_BAL, temporaryBal).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_SYM, []byte("")).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_BAL, []byte("")).Return(nil) h := &Handlers{ userdataStore: mockDataStore, @@ -1919,7 +1918,7 @@ func TestFetchCustodialBalances(t *testing.T) { } // Set up the expected behavior of the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) // Call the method diff --git a/internal/testutil/mocks/userdbmock.go b/internal/testutil/mocks/userdbmock.go index ff3f18d..7892179 100644 --- a/internal/testutil/mocks/userdbmock.go +++ b/internal/testutil/mocks/userdbmock.go @@ -4,7 +4,7 @@ import ( "context" "git.defalsify.org/vise.git/db" - "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/common" "github.com/stretchr/testify/mock" ) @@ -13,12 +13,12 @@ type MockUserDataStore struct { mock.Mock } -func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ utils.DataTyp) ([]byte, error) { +func (m *MockUserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.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 { +func (m *MockUserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { args := m.Called(ctx, sessionId, typ, value) return args.Error(0) } diff --git a/internal/utils/userStore.go b/internal/utils/userStore.go index a1485b1..13682b7 100644 --- a/internal/utils/userStore.go +++ b/internal/utils/userStore.go @@ -3,13 +3,15 @@ package utils import ( "context" + "git.grassecon.net/urdt/ussd/common" + "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 + ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) + WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error } type UserDataStore struct { @@ -17,16 +19,16 @@ type UserDataStore struct { } // 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) { +func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := PackKey(typ, []byte(sessionId)) + k := common.PackKey(typ, []byte(sessionId)) return store.Get(ctx, k) } -func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { +func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := PackKey(typ, []byte(sessionId)) + k := common.PackKey(typ, []byte(sessionId)) return store.Put(ctx, k, value) } From c95b97cb148114aed46a8aa84da8ec9faf2dcaee Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 01:45:38 +0000 Subject: [PATCH 030/332] Export user data store --- .../utils/userStore.go => common/user_store.go | 16 +++++++--------- internal/handlers/ussd/menuhandler.go | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) rename internal/utils/userStore.go => common/user_store.go (55%) diff --git a/internal/utils/userStore.go b/common/user_store.go similarity index 55% rename from internal/utils/userStore.go rename to common/user_store.go index 13682b7..8d982ab 100644 --- a/internal/utils/userStore.go +++ b/common/user_store.go @@ -1,17 +1,15 @@ -package utils +package common import ( "context" - "git.grassecon.net/urdt/ussd/common" - "git.defalsify.org/vise.git/db" ) type DataStore interface { db.Db - ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) - WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error + ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) + WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error } type UserDataStore struct { @@ -19,16 +17,16 @@ type UserDataStore struct { } // ReadEntry retrieves an entry from the store based on the provided parameters. -func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ common.DataTyp) ([]byte, error) { +func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := common.PackKey(typ, []byte(sessionId)) + k := PackKey(typ, []byte(sessionId)) return store.Get(ctx, k) } -func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ common.DataTyp, value []byte) error { +func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) - k := common.PackKey(typ, []byte(sessionId)) + k := PackKey(typ, []byte(sessionId)) return store.Put(ctx, k, value) } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index e2bcfaf..303bea9 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -63,7 +63,7 @@ type Handlers struct { pe *persist.Persister st *state.State ca cache.Memory - userdataStore utils.DataStore + userdataStore common.DataStore flagManager *asm.FlagParser accountService server.AccountServiceInterface } @@ -72,7 +72,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } - userDb := &utils.UserDataStore{ + userDb := &common.UserDataStore{ Db: userdataStore, } h := &Handlers{ From b404ae95fbfa4fdec0b7c84e7c7486618a754eca Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:30:38 +0300 Subject: [PATCH 031/332] setup an admin store based on fs --- internal/utils/adminstore.go | 111 ++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index ddd6bd9..43a3bfd 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -1,80 +1,85 @@ package utils import ( - "bufio" - "log" + "context" + "encoding/json" "os" - "strconv" - "strings" - "git.grassecon.net/urdt/ussd/initializers" + "git.defalsify.org/vise.git/db" + fsdb "git.defalsify.org/vise.git/db/fs" + "git.defalsify.org/vise.git/logging" ) -type AdminStore struct { - filePath string +var ( + logg = logging.NewVanilla().WithDomain("adminstore") +) + +type Admin struct { + PhoneNumber string `json:"phonenumber"` } -// Creates a new Admin store -func NewAdminStore(filePath string) *AdminStore { - return &AdminStore{filePath: filePath} +type Config struct { + Admins []Admin `json:"admins"` +} + +type AdminStore struct { + ctx context.Context + fsStore db.Db +} + +func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { + fsStore, err := getFsStore(ctx, fileName) + if err != nil { + return nil, err + } + return &AdminStore{ctx: ctx, fsStore: fsStore}, nil +} + +func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { + fsStore := fsdb.NewFsDb() + err := fsStore.Connect(ctx, connectStr) + fsStore.SetPrefix(db.DATATYPE_USERDATA) + if err != nil { + return nil, err + } + return fsStore, nil } // Seed initializes a list of phonenumbers with admin privileges func (as *AdminStore) Seed() error { - var adminNumbers []int64 + var config Config - numbersEnv := initializers.GetEnv("ADMIN_NUMBERS", "") - for _, numStr := range strings.Split(numbersEnv, ",") { - if num, err := strconv.ParseInt(strings.TrimSpace(numStr), 10, 64); err == nil { - adminNumbers = append(adminNumbers, num) - } else { - log.Printf("Skipping invalid number: %s", numStr) - } - } - file, err := os.Create(as.filePath) + store := as.fsStore + defer store.Close() + + data, err := os.ReadFile("admin_numbers.json") if err != nil { return err } - defer file.Close() - - writer := bufio.NewWriter(file) - for _, num := range adminNumbers { - _, err := writer.WriteString(strconv.FormatInt(num, 10) + "\n") + if err := json.Unmarshal(data, &config); err != nil { + return err + } + for _, admin := range config.Admins { + err := store.Put(as.ctx, []byte(admin.PhoneNumber), []byte("1")) if err != nil { + logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) return err } } - return writer.Flush() + return nil } -func (as *AdminStore) load() ([]int64, error) { - file, err := os.Open(as.filePath) +// Checks if the given sessionId is listed as an admin. +func (as *AdminStore) IsAdmin(sessionId string) (bool, error) { + _, err := as.fsStore.Get(as.ctx, []byte(sessionId)) if err != nil { - return nil, err - } - defer file.Close() - - var numbers []int64 - scanner := bufio.NewScanner(file) - for scanner.Scan() { - num, err := strconv.ParseInt(scanner.Text(), 10, 64) - if err != nil { - return nil, err - } - numbers = append(numbers, num) - } - return numbers, scanner.Err() -} - -func (as *AdminStore) IsAdmin(phoneNumber int64) (bool, error) { - phoneNumbers, err := as.load() - if err != nil { - return false, err - } - for _, phonenumber := range phoneNumbers { - if phonenumber == phoneNumber { - return true, nil + if db.IsNotFound(err) { + logg.Printf(logging.LVL_INFO, "Returning false because session id was not found") + return false, nil + } else { + return false, err } } - return false, nil + + return true, nil } From c42b1cd66b9dd63b5f31ca5c60bc483d2230bee1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:32:01 +0300 Subject: [PATCH 032/332] provide required handler functions and admin store --- internal/handlers/handlerservice.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index b4cd592..247e6c5 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -8,6 +8,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/internal/utils" ) type HandlerService interface { @@ -28,6 +29,7 @@ type LocalHandlerService struct { DbRs *resource.DbResource Pe *persist.Persister UserdataStore *db.Db + AdminStore *utils.AdminStore Cfg engine.Config Rs resource.Resource } @@ -53,8 +55,12 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } +func (ls *LocalHandlerService) SetAdminStore(adminstore *utils.AdminStore) { + ls.AdminStore = adminstore +} + func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, accountService) + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) if err != nil { return nil, err } @@ -103,6 +109,8 @@ func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceIn ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) + ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) + ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) return ussdHandlers, nil } From c2068db050a931b6f6b9e75bdf876f612d072087 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:33:22 +0300 Subject: [PATCH 033/332] update handler functions --- services/registration/confirm_others_new_pin.vis | 4 ++-- services/registration/enter_others_new_pin.vis | 4 ++-- services/registration/pin_reset_result.vis | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index be6734f..319346a 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,9 +1,9 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 -LOAD save_temporary_pin 6 -RELOAD save_temporary_pin CATCH pin_reset_result flag_account_authorized 1 +LOAD save_others_temporary_pin 6 +RELOAD save_others_temporary_pin MOUT back 0 HALT INCMP _ 0 diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index aa2818d..16db41c 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -2,7 +2,7 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 HALT -INCMP _ 0 -LOAD verify_new_pin 0 +LOAD verify_new_pin 6 RELOAD verify_new_pin +INCMP _ 0 INCMP * confirm_others_new_pin diff --git a/services/registration/pin_reset_result.vis b/services/registration/pin_reset_result.vis index ed71200..34b9789 100644 --- a/services/registration/pin_reset_result.vis +++ b/services/registration/pin_reset_result.vis @@ -1,5 +1,6 @@ LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number +LOAD reset_others_pin 6 MOUT back 0 MOUT quit 9 HALT From dc418771a79dbb8653fc3251554b0d1591574d4d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:33:48 +0300 Subject: [PATCH 034/332] attach an admin store for phone numbers --- cmd/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 4d23011..7dd3cd6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -49,7 +49,7 @@ func main() { ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") - as := utils.NewAdminStore("admin_numbers.txt") + as, _ := utils.NewAdminStore(ctx, "admin_numbers") as.Seed() cfg := engine.Config{ @@ -94,6 +94,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdatastore) + lhs.SetAdminStore(as) lhs.SetPersister(pe) if err != nil { From 017691a40cac3bab37121d875a3da914eca8bd53 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:41:14 +0300 Subject: [PATCH 035/332] define structure for admin numbers --- admin_numbers.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 admin_numbers.json diff --git a/admin_numbers.json b/admin_numbers.json new file mode 100644 index 0000000..ca58a23 --- /dev/null +++ b/admin_numbers.json @@ -0,0 +1,7 @@ +{ + "admins": [ + { + "phonenumber" : "" + } + ] +} \ No newline at end of file From 888d3befe99437be89ade4cd60915ca3730899c4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:50:12 +0300 Subject: [PATCH 036/332] add actual pin reset functionality --- internal/handlers/ussd/menuhandler.go | 61 ++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 70510ee..8b0c385 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -69,11 +69,12 @@ type Handlers struct { st *state.State ca cache.Memory userdataStore utils.DataStore + adminstore *utils.AdminStore flagManager *asm.FlagParser accountService server.AccountServiceInterface } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService server.AccountServiceInterface) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } @@ -83,6 +84,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService s h := &Handlers{ userdataStore: userDb, flagManager: appFlags, + adminstore: adminstore, accountService: accountService, } return h, nil @@ -120,9 +122,7 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource sessionId, _ := ctx.Value("SessionId").(string) flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") - number, _ := strconv.ParseInt(sessionId, 10, 64) - as := utils.NewAdminStore("admin_numbers.txt") - isAdmin, _ := as.IsAdmin(number) + isAdmin, _ := h.adminstore.IsAdmin(sessionId) if isAdmin { r.FlagSet = append(r.FlagSet, flag_admin_privilege) @@ -220,7 +220,11 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + if err != nil { + return res, err + } + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN) if err != nil { return res, err } @@ -272,6 +276,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt } res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + fmt.Println("Saving:", string(accountPIN)) store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) @@ -282,6 +287,29 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } +func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var err error + + store := h.userdataStore + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + temporaryPin := string(input) + blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + + if err != nil { + return res, err + } + err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN, []byte(temporaryPin)) + if err != nil { + return res, err + } + + return res, nil +} + func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -672,7 +700,6 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by var res resource.Result flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") - res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) return res, nil } @@ -755,6 +782,28 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input return res, nil } +func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + store := h.userdataStore + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + if err != nil { + return res, err + } + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_PIN) + if err != nil { + return res, err + } + err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + if err != nil { + return res, nil + } + return res, nil +} + func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") From cf523e30f89571ed58c933bbd2b0162034947cdc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 14:50:31 +0300 Subject: [PATCH 037/332] update handler in test --- internal/handlers/ussd/menuhandler_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 28d25e8..d23408a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -39,7 +39,7 @@ func TestNewHandlers(t *testing.T) { } t.Run("Valid UserDataStore", func(t *testing.T) { mockStore := &mocks.MockUserDataStore{} - handlers, err := NewHandlers(fm.parser, mockStore, &accountService) + handlers, err := NewHandlers(fm.parser, mockStore, nil, &accountService) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -55,7 +55,7 @@ func TestNewHandlers(t *testing.T) { t.Run("Nil UserDataStore", func(t *testing.T) { appFlags := &asm.FlagParser{} - handlers, err := NewHandlers(appFlags, nil, &accountService) + handlers, err := NewHandlers(appFlags, nil, nil, &accountService) if err == nil { t.Fatal("expected an error, got none") @@ -1481,8 +1481,8 @@ func TestValidateAmount(t *testing.T) { }, }, { - name: "Test with invalid amount format", - input: []byte("0.02ms"), + name: "Test with invalid amount format", + input: []byte("0.02ms"), activeBal: []byte("5"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_amount}, From 14648fec6cfcc348bf5e38ff6e0eb17a53cc015e Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 12:02:16 +0000 Subject: [PATCH 038/332] Edit comment --- common/user_store.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/user_store.go b/common/user_store.go index 8d982ab..4b8409f 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -16,7 +16,7 @@ type UserDataStore struct { db.Db } -// ReadEntry retrieves an entry from the store based on the provided parameters. +// ReadEntry retrieves an entry to the userdata store. func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) @@ -24,6 +24,7 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ return store.Get(ctx, k) } +// WriteEntry adds an entry to the userdata store. func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) From ff26ccc545d1b09456a1875d8e59ef290a15f349 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 30 Oct 2024 13:09:15 +0000 Subject: [PATCH 039/332] Expose api interface --- cmd/africastalking/main.go | 4 ++-- cmd/async/main.go | 4 ++-- cmd/http/main.go | 4 ++-- cmd/main.go | 4 ++-- internal/handlers/handlerservice.go | 5 +++-- internal/handlers/ussd/menuhandler.go | 6 +++--- internal/testutil/TestEngine.go | 6 +++--- internal/testutil/testtag/offlinetest.go | 4 ++-- {internal/handlers/server => remote}/accountservice.go | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) rename {internal/handlers/server => remote}/accountservice.go (99%) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 79fb091..7c98f38 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -19,9 +19,9 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -139,7 +139,7 @@ func main() { os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/cmd/async/main.go b/cmd/async/main.go index 902dfc7..b879c68 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -16,8 +16,8 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -106,7 +106,7 @@ func main() { lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/http/main.go b/cmd/http/main.go index ece882e..638ca87 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -18,9 +18,9 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/handlers/server" httpserver "git.grassecon.net/urdt/ussd/internal/http" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -99,7 +99,7 @@ func main() { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/cmd/main.go b/cmd/main.go index 21ca0c3..13ceb3c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -13,8 +13,8 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -97,7 +97,7 @@ func main() { os.Exit(1) } - accountService := server.AccountService{} + accountService := remote.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 311e694..b409fa6 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -6,8 +6,9 @@ import ( "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" - "git.grassecon.net/urdt/ussd/internal/handlers/server" + "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/remote" ) type HandlerService interface { @@ -53,7 +54,7 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } -func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { +func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore,accountService) if err != nil { return nil, err diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 303bea9..aa5c1aa 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -19,9 +19,9 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -65,10 +65,10 @@ type Handlers struct { ca cache.Memory userdataStore common.DataStore flagManager *asm.FlagParser - accountService server.AccountServiceInterface + accountService remote.AccountServiceInterface } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService server.AccountServiceInterface) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, accountService remote.AccountServiceInterface) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index bd6bc7f..fa585ae 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -11,11 +11,11 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers" - "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/internal/testutil/testtag" testdataloader "github.com/peteole/testdata-loader" + "git.grassecon.net/urdt/ussd/remote" ) var ( @@ -83,7 +83,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { } if testtag.AccountService == nil { - testtag.AccountService = &server.AccountService{} + testtag.AccountService = &remote.AccountService{} } switch testtag.AccountService.(type) { @@ -91,7 +91,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { go func() { eventChannel <- false }() - case *server.AccountService: + case *remote.AccountService: go func() { time.Sleep(5 * time.Second) // Wait for 5 seconds eventChannel <- true diff --git a/internal/testutil/testtag/offlinetest.go b/internal/testutil/testtag/offlinetest.go index 70e2e80..831bf09 100644 --- a/internal/testutil/testtag/offlinetest.go +++ b/internal/testutil/testtag/offlinetest.go @@ -3,10 +3,10 @@ package testtag import ( - "git.grassecon.net/urdt/ussd/internal/handlers/server" + "git.grassecon.net/urdt/ussd/remote" accountservice "git.grassecon.net/urdt/ussd/internal/testutil/testservice" ) var ( - AccountService server.AccountServiceInterface = &accountservice.TestAccountService{} + AccountService remote.AccountServiceInterface = &accountservice.TestAccountService{} ) diff --git a/internal/handlers/server/accountservice.go b/remote/accountservice.go similarity index 99% rename from internal/handlers/server/accountservice.go rename to remote/accountservice.go index 890d1db..236ccf4 100644 --- a/internal/handlers/server/accountservice.go +++ b/remote/accountservice.go @@ -1,4 +1,4 @@ -package server +package remote import ( "context" From d93a26f9b0d2ae02c9798825efac879d09630cf0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 17:58:52 +0300 Subject: [PATCH 040/332] remove function exec after HALT --- services/registration/enter_other_number.vis | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/registration/enter_other_number.vis b/services/registration/enter_other_number.vis index e2fa243..0957165 100644 --- a/services/registration/enter_other_number.vis +++ b/services/registration/enter_other_number.vis @@ -4,7 +4,4 @@ RELOAD reset_account_authorized MOUT back 0 HALT INCMP _ 0 -LOAD validate_blocked_number 20 -RELOAD validate_blocked_number -CATCH unregistered_number flag_unregistered_number 1 INCMP enter_others_new_pin * From 41585f831c3e38978c99d34a20859e4ab8ef6e71 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:00:38 +0300 Subject: [PATCH 041/332] move catch and load to next node --- services/registration/enter_others_new_pin.vis | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 16db41c..49c2426 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,3 +1,6 @@ +LOAD validate_blocked_number 20 +RELOAD validate_blocked_number +CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 MAP retrieve_blocked_number MOUT back 0 From 7c823e07ca6c4f0175e58902522f1de97a104599 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:01:20 +0300 Subject: [PATCH 042/332] move to root node after on back --- services/registration/unregistered_number.vis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index 114113f..0c66912 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -1,7 +1,7 @@ LOAD reset_unregistered_number 0 RELOAD reset_unregistered_number -MOUT retry 1 +MOUT back 1 MOUT quit 9 HALT -INCMP _ 1 +INCMP ^ 1 INCMP quit 9 From ea4c6d9314158ecb7a6625b9c6544285da4a7bd1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 18:01:43 +0300 Subject: [PATCH 043/332] check for phone number validity --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8b0c385..4d6f724 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -816,7 +816,6 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input var err error flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") - store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -825,6 +824,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) if !isValidPhoneNumber(blockedNumber) { + res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } if err != nil { From 833d52a558b2e20359ebe4beeb482dd7e2757397 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 21:26:11 +0300 Subject: [PATCH 044/332] remove print message --- internal/handlers/ussd/menuhandler.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index bbcfd58..3ffa61b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -19,9 +19,9 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" + "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" - "git.grassecon.net/urdt/ussd/common" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -283,10 +283,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil } - res.FlagReset = append(res.FlagReset, flag_incorrect_pin) - fmt.Println("Saving:", string(accountPIN)) - store := h.userdataStore err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) if err != nil { From 8093eae61aeb35e0a76fce275fca04fb950adedb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 21:26:52 +0300 Subject: [PATCH 045/332] use 0 instead of 1 for back --- services/registration/unregistered_number.vis | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/unregistered_number.vis b/services/registration/unregistered_number.vis index 0c66912..0ff96be 100644 --- a/services/registration/unregistered_number.vis +++ b/services/registration/unregistered_number.vis @@ -1,7 +1,7 @@ LOAD reset_unregistered_number 0 RELOAD reset_unregistered_number -MOUT back 1 +MOUT back 0 MOUT quit 9 HALT -INCMP ^ 1 +INCMP ^ 0 INCMP quit 9 From c6ca3f6be4fb405ebf0a620a26adc677242d8d8f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 22:32:38 +0300 Subject: [PATCH 046/332] fix sink error --- services/registration/confirm_others_new_pin.vis | 2 +- services/registration/enter_others_new_pin.vis | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index 319346a..f63d7ae 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,4 +1,4 @@ -LOAD retrieve_blocked_number 0 +RELOAD retrieve_blocked_number MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 CATCH pin_reset_result flag_account_authorized 1 diff --git a/services/registration/enter_others_new_pin.vis b/services/registration/enter_others_new_pin.vis index 49c2426..7711c97 100644 --- a/services/registration/enter_others_new_pin.vis +++ b/services/registration/enter_others_new_pin.vis @@ -1,7 +1,8 @@ -LOAD validate_blocked_number 20 +LOAD validate_blocked_number 6 RELOAD validate_blocked_number CATCH unregistered_number flag_unregistered_number 1 LOAD retrieve_blocked_number 0 +RELOAD retrieve_blocked_number MAP retrieve_blocked_number MOUT back 0 HALT From d4341940218dc1b24795ccdb10165fc3098242a0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 30 Oct 2024 23:21:15 +0300 Subject: [PATCH 047/332] catch incorrect pin entry --- services/registration/confirm_others_new_pin.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/confirm_others_new_pin.vis b/services/registration/confirm_others_new_pin.vis index f63d7ae..9132dc4 100644 --- a/services/registration/confirm_others_new_pin.vis +++ b/services/registration/confirm_others_new_pin.vis @@ -1,3 +1,4 @@ +CATCH pin_entry flag_incorrect_pin 1 RELOAD retrieve_blocked_number MAP retrieve_blocked_number CATCH invalid_others_pin flag_valid_pin 0 From 1e638238ed368319fb84ed9deb8d90d0df55b28d Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 01:28:37 +0000 Subject: [PATCH 048/332] WIP refactor models usage --- config/config.go | 67 ++++++-- internal/handlers/ussd/menuhandler.go | 20 +-- internal/handlers/ussd/menuhandler_test.go | 1 - internal/models/accountresponse.go | 10 +- internal/models/balanceresponse.go | 17 +- internal/models/trackstatusresponse.go | 23 ++- internal/models/vouchersresponse.go | 13 -- internal/testutil/mocks/servicemock.go | 27 ++-- .../testservice/TestAccountService.go | 78 +++------ remote/accountservice.go | 149 ++++++++++++------ 10 files changed, 229 insertions(+), 176 deletions(-) diff --git a/config/config.go b/config/config.go index 43466c3..a5c89a7 100644 --- a/config/config.go +++ b/config/config.go @@ -1,18 +1,67 @@ package config -import "git.grassecon.net/urdt/ussd/initializers" +import ( + "net/url" + + "git.grassecon.net/urdt/ussd/initializers" +) + +const ( + createAccountPath = "/api/v2/account/create" + trackStatusPath = "/api/track" + balancePathPrefix = "/api/account" + trackPath = "/api/v2/account/status" + voucherHoldingsPathPrefix = "/api/v1/holdings" + voucherTransfersPathPrefix = "/api/v1/transfers/last10" +) + +var ( + custodialURLBase string + dataURLBase string + CustodialAPIKey string + DataAPIKey string +) var ( CreateAccountURL string TrackStatusURL string - BalanceURL string - TrackURL string + BalanceURL string + TrackURL string + VoucherHoldingsURL string + VoucherTransfersURL string ) -// LoadConfig initializes the configuration values after environment variables are loaded. -func LoadConfig() { - CreateAccountURL = initializers.GetEnv("CREATE_ACCOUNT_URL", "http://localhost:5003/api/v2/account/create") - TrackStatusURL = initializers.GetEnv("TRACK_STATUS_URL", "https://custodial.sarafu.africa/api/track/") - BalanceURL = initializers.GetEnv("BALANCE_URL", "https://custodial.sarafu.africa/api/account/status/") - TrackURL = initializers.GetEnv("TRACK_URL", "http://localhost:5003/api/v2/account/status") +func setBase() error { + var err error + + custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") + dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") + CustodialAPIKey = initializers.GetEnv("CUSTODIAL_API_KEY", "xd") + DataAPIKey = initializers.GetEnv("DATA_API_KEY", "xd") + + _, err = url.JoinPath(custodialURLBase, "/foo") + if err != nil { + return err + } + _, err = url.JoinPath(dataURLBase, "/bar") + if err != nil { + return err + } + return nil +} + +// LoadConfig initializes the configuration values after environment variables are loaded. +func LoadConfig() error { + err := setBase() + if err != nil { + return err + } + CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath) + TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) + BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) + TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) + VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) + VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) + + return nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f568e6a..5f6f81a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -149,12 +149,12 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") - okResponse, err := h.accountService.CreateAccount(ctx) + r, err := h.accountService.CreateAccount(ctx) if err != nil { return err } - trackingId := okResponse.Result["trackingId"].(string) - publicKey := okResponse.Result["publicKey"].(string) + trackingId := r.TrackingId + publicKey := r.PublicKey data := map[common.DataTyp]string{ common.DATA_TRACKING_ID: trackingId, @@ -682,6 +682,7 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") sessionId, ok := ctx.Value("SessionId").(string) @@ -699,14 +700,13 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) if err != nil { - return res, nil - } - if !balanceResponse.Ok { res.FlagSet = append(res.FlagSet, flag_api_error) return res, nil } res.FlagReset = append(res.FlagReset, flag_api_error) - balance := balanceResponse.Result.Balance + + //balance := balanceResponse.Result.Balance + balance := balanceResponse.Balance switch balanceType { case "my": @@ -1066,13 +1066,13 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by } // Return if there is no voucher - if len(vouchersResp.Result.Holdings) == 0 { + if len(vouchersResp) == 0 { res.FlagSet = append(res.FlagSet, flag_no_active_voucher) return res, nil } // Use only the first voucher - firstVoucher := vouchersResp.Result.Holdings[0] + firstVoucher := vouchersResp[0] defaultSym := firstVoucher.TokenSymbol defaultBal := firstVoucher.Balance @@ -1119,7 +1119,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - data := utils.ProcessVouchers(vouchersResp.Result.Holdings) + data := utils.ProcessVouchers(vouchersResp) // Store all voucher data dataMap := map[string]string{ diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index bae95a8..5f3c953 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1159,7 +1159,6 @@ func TestCheckAccountStatus(t *testing.T) { // Assert that expectations were met mockDataStore.AssertExpectations(t) - }) } diff --git a/internal/models/accountresponse.go b/internal/models/accountresponse.go index efcc30b..278e0e9 100644 --- a/internal/models/accountresponse.go +++ b/internal/models/accountresponse.go @@ -1,10 +1,6 @@ package models -type AccountResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` // Include the description field - Result struct { - PublicKey string `json:"publicKey"` - TrackingId string `json:"trackingId"` - } `json:"result"` +type AccountResult struct { + PublicKey string `json:"publicKey"` + TrackingId string `json:"trackingId"` } diff --git a/internal/models/balanceresponse.go b/internal/models/balanceresponse.go index 57c8e5a..3d57c95 100644 --- a/internal/models/balanceresponse.go +++ b/internal/models/balanceresponse.go @@ -3,10 +3,15 @@ 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"` +//type BalanceResponse struct { +// Ok bool `json:"ok"` +// Result struct { +// Balance string `json:"balance"` +// Nonce json.Number `json:"nonce"` +// } `json:"result"` +//} +// +type BalanceResult struct { + Balance string `json:"balance"` + Nonce json.Number `json:"nonce"` } diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6b96d90..6130c6e 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -4,23 +4,18 @@ import ( "encoding/json" "time" ) -type Transaction struct { +//type Transaction struct { +// CreatedAt time.Time `json:"createdAt"` +// Status string `json:"status"` +// TransferValue json.Number `json:"transferValue"` +// TxHash string `json:"txHash"` +// TxType string `json:"txType"` +//} + +type TrackStatusResult struct { CreatedAt time.Time `json:"createdAt"` Status string `json:"status"` TransferValue json.Number `json:"transferValue"` TxHash string `json:"txHash"` TxType string `json:"txType"` } - -type TrackStatusResponse struct { - Ok bool `json:"ok"` - Result struct { - Transaction struct { - CreatedAt time.Time `json:"createdAt"` - Status string `json:"status"` - TransferValue json.Number `json:"transferValue"` - TxHash string `json:"txHash"` - TxType string `json:"txType"` - } - } `json:"result"` -} diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go index 09b085d..2640e7f 100644 --- a/internal/models/vouchersresponse.go +++ b/internal/models/vouchersresponse.go @@ -1,14 +1 @@ package models - -import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - -type VoucherHoldingResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - Result VoucherResult `json:"result"` -} - -// VoucherResult holds the list of token holdings -type VoucherResult struct { - Holdings []dataserviceapi.TokenHoldings `json:"holdings"` -} diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index de0e99a..3b36ba7 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -5,6 +5,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/models" "github.com/grassrootseconomics/eth-custodial/pkg/api" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/stretchr/testify/mock" ) @@ -13,28 +14,28 @@ type MockAccountService struct { mock.Mock } -func (m *MockAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { +func (m *MockAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { args := m.Called() - return args.Get(0).(*api.OKResponse), args.Error(1) + return args.Get(0).(*models.AccountResult), args.Error(1) } -func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { +func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { args := m.Called(publicKey) - return args.Get(0).(*models.BalanceResponse), args.Error(1) + return args.Get(0).(*models.BalanceResult), args.Error(1) } -func (m *MockAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { +func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*api.OKResponse, error) { args := m.Called(trackingId) - return args.Get(0).(*models.TrackStatusResponse), args.Error(1) -} - -func (m *MockAccountService) TrackAccountStatus(ctx context.Context,publicKey string) (*api.OKResponse, error) { - args := m.Called(publicKey) return args.Get(0).(*api.OKResponse), args.Error(1) } - -func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { +func (m *MockAccountService) CheckAccountStatus(ctx context.Context,publicKey string) (*models.TrackStatusResult, error) { args := m.Called(publicKey) - return args.Get(0).(*models.VoucherHoldingResponse), args.Error(1) + return args.Get(0).(*models.TrackStatusResult), args.Error(1) +} + + +func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + args := m.Called(publicKey) + return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) } diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 745b80d..885e63f 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -13,54 +13,29 @@ import ( type TestAccountService struct { } -func (tas *TestAccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "trackingId": "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", - "publicKey": "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", - }, +func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { + return &models.AccountResult { + TrackingId: "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", + PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", }, nil } -func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { - balanceResponse := &models.BalanceResponse{ - Ok: true, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, +func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { + balanceResponse := &models.BalanceResult { + Balance: "0.003 CELO", + Nonce: json.Number("0"), } - return balanceResponse, nil } -func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { - trackResponse := &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - } - return trackResponse, nil +func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { + return &models.TrackStatusResult { + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", + }, nil } func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { @@ -73,18 +48,13 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey }, nil } -func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { - return &models.VoucherHoldingResponse{ - Ok: true, - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{ - { - ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", - TokenSymbol: "SRF", - TokenDecimals: "6", - Balance: "2745987", - }, - }, +func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + return []dataserviceapi.TokenHoldings { + dataserviceapi.TokenHoldings { + ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", + TokenSymbol: "SRF", + TokenDecimals: "6", + Balance: "2745987", }, - }, nil + }, nil } diff --git a/remote/accountservice.go b/remote/accountservice.go index 236ccf4..c11910f 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -4,27 +4,29 @@ import ( "context" "encoding/json" "errors" - "fmt" "io" "net/http" - "os" + "net/url" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" + "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" ) var ( - okResponse api.OKResponse - errResponse api.ErrResponse ) type AccountServiceInterface interface { - CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) - CreateAccount(ctx context.Context) (*api.OKResponse, error) - CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) + //CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) + CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) + CreateAccount(ctx context.Context) (*models.AccountResult, error) + //CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) + // TODO: poorly named method seems to be checking transaction + CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) - FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) + FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) + //FetchVouchers(ctx context.Context, publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -39,32 +41,38 @@ type AccountService struct { // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil -func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) { - resp, err := http.Get(config.BalanceURL + trackingId) - if err != nil { - return nil, err - } - defer resp.Body.Close() +func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { + var r models.TrackStatusResult - body, err := io.ReadAll(resp.Body) + ep, err := url.JoinPath(config.TrackURL, trackingId) if err != nil { return nil, err } - var trackResp models.TrackStatusResponse - err = json.Unmarshal(body, &trackResp) + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } - return &trackResp, nil + _, err = doCustodialRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil } func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { + var okResponse api.OKResponse + var errResponse api.ErrResponse var err error - // Construct the URL with the path parameter - url := fmt.Sprintf("%s/%s", config.TrackURL, publicKey) - req, err := http.NewRequest("GET", url, nil) + + ep, err := url.JoinPath(config.TrackURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } @@ -104,41 +112,83 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. -func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) { - resp, err := http.Get(config.BalanceURL + publicKey) +func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { + var balanceResult models.BalanceResult + + ep, err := url.JoinPath(config.BalanceURL, publicKey) if err != nil { return nil, err } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } - var balanceResp models.BalanceResponse - err = json.Unmarshal(body, &balanceResp) - if err != nil { - return nil, err - } - return &balanceResp, nil + + _, err = doCustodialRequest(ctx, req, &balanceResult) + return &balanceResult, err } + // CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. // If there is an error during the request or processing, this will be nil. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil. -func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, error) { - var err error - +func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { + var r models.AccountResult // Create a new request req, err := http.NewRequest("POST", config.CreateAccountURL, nil) if err != nil { return nil, err } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-GE-KEY", "xd") + _, err = doCustodialRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil +} + +// FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint +// Parameters: +// - publicKey: The public key associated with the account. +func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { + var r []dataserviceapi.TokenHoldings + + req, err := http.NewRequest("GET", config.VoucherHoldingsURL, nil) + if err != nil { + return nil, err + } + + _, err = doDataRequest(ctx, req, r) + if err != nil { + return nil, err + } + + return r, nil + +// +// file, err := os.Open("sample_tokens.json") +// if err != nil { +// return nil, err +// } +// defer file.Close() +// var holdings models.VoucherHoldingResponse +// +// if err := json.NewDecoder(file).Decode(&holdings); err != nil { +// return nil, err +// } +// return &holdings, nil +} + +func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + var okResponse api.OKResponse + var errResponse api.ErrResponse + + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { errResponse.Description = err.Error() @@ -165,21 +215,22 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*api.OKResponse, e return nil, errors.New("Empty api result") } return &okResponse, nil -} -// FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint -// Parameters: -// - publicKey: The public key associated with the account. -func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) (*models.VoucherHoldingResponse, error) { - file, err := os.Open("sample_tokens.json") + v, err := json.Marshal(okResponse.Result) if err != nil { return nil, err } - defer file.Close() - var holdings models.VoucherHoldingResponse - if err := json.NewDecoder(file).Decode(&holdings); err != nil { - return nil, err - } - return &holdings, nil + err = json.Unmarshal(v, &rcpt) + return &okResponse, err +} + +func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + req.Header.Set("X-GE-KEY", config.CustodialAPIKey) + return doRequest(ctx, req, rcpt) +} + +func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { + req.Header.Set("X-GE-KEY", config.DataAPIKey) + return doRequest(ctx, req, rcpt) } From a48170321c03aa232aabf7065f98c08c947e6e9f Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 01:51:36 +0000 Subject: [PATCH 049/332] Finish refactor result models --- internal/handlers/ussd/menuhandler_test.go | 136 ++++++--------------- 1 file changed, 36 insertions(+), 100 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 5f3c953..77447ca 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -88,18 +88,14 @@ func TestCreateAccount(t *testing.T) { tests := []struct { name string - serverResponse *api.OKResponse + serverResponse *models.AccountResult expectedResult resource.Result }{ { name: "Test account creation success", - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation successed", - Result: map[string]any{ - "trackingId": "1234567890", - "publicKey": "0xD3adB33f", - }, + serverResponse: &models.AccountResult{ + TrackingId: "1234567890", + PublicKey: "0xD3adB33f", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_created}, @@ -119,9 +115,9 @@ func TestCreateAccount(t *testing.T) { flagManager: fm.parser, } - publicKey := tt.serverResponse.Result["publicKey"].(string) + publicKey := tt.serverResponse.PublicKey data := map[common.DataTyp]string{ - common.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), + common.DATA_TRACKING_ID: tt.serverResponse.TrackingId, common.DATA_PUBLIC_KEY: publicKey, } @@ -1055,7 +1051,7 @@ func TestCheckAccountStatus(t *testing.T) { name string input []byte serverResponse *api.OKResponse - response *models.TrackStatusResponse + response *models.TrackStatusResult expectedResult resource.Result }{ { @@ -1068,25 +1064,12 @@ func TestCheckAccountStatus(t *testing.T) { "active": true, }, }, - response: &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, + response: &models.TrackStatusResult { + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, @@ -1096,25 +1079,12 @@ func TestCheckAccountStatus(t *testing.T) { { name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), - response: &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, + response: &models.TrackStatusResult{ + CreatedAt: time.Now(), + Status: "SUCCESS", + TransferValue: json.Number("0.5"), + TxHash: "0x123abc456def", + TxType: "transfer", }, serverResponse: &api.OKResponse{ Ok: true, @@ -1140,7 +1110,7 @@ func TestCheckAccountStatus(t *testing.T) { flagManager: fm.parser, } - status := tt.response.Result.Transaction.Status + status := tt.response.Status // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) @@ -1833,36 +1803,14 @@ func TestFetchCustodialBalances(t *testing.T) { tests := []struct { name string - balanceResonse *models.BalanceResponse + balanceResponse *models.BalanceResult expectedResult resource.Result }{ { name: "Test when fetch custodial balances is not a success", - balanceResonse: &models.BalanceResponse{ - Ok: false, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, - }, - expectedResult: resource.Result{ - FlagSet: []uint32{flag_api_error}, - }, - }, - { - name: "Test when fetch custodial balances is a success", - balanceResonse: &models.BalanceResponse{ - Ok: true, - Result: struct { - Balance string `json:"balance"` - Nonce json.Number `json:"nonce"` - }{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, + balanceResponse: &models.BalanceResult{ + Balance: "0.003 CELO", + Nonce: json.Number("0"), }, expectedResult: resource.Result{ FlagReset: []uint32{flag_api_error}, @@ -1886,7 +1834,7 @@ func TestFetchCustodialBalances(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) + mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResponse, nil) // Call the method res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) @@ -1918,35 +1866,24 @@ func TestSetDefaultVoucher(t *testing.T) { tests := []struct { name string - vouchersResp *models.VoucherHoldingResponse + vouchersResp []dataserviceapi.TokenHoldings expectedResult resource.Result }{ { name: "Test set default voucher when no active voucher exists", - vouchersResp: &models.VoucherHoldingResponse{ - Ok: true, - Description: "Vouchers fetched successfully", - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{ - { - ContractAddress: "0x123", - TokenSymbol: "TOKEN1", - TokenDecimals: "18", - Balance: "100", - }, - }, + vouchersResp: []dataserviceapi.TokenHoldings { + dataserviceapi.TokenHoldings{ + ContractAddress: "0x123", + TokenSymbol: "TOKEN1", + TokenDecimals: "18", + Balance: "100", }, }, expectedResult: resource.Result{}, }, { name: "Test no vouchers available", - vouchersResp: &models.VoucherHoldingResponse{ - Ok: true, - Description: "No vouchers available", - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{}, - }, + vouchersResp: []dataserviceapi.TokenHoldings { }, expectedResult: resource.Result{ FlagSet: []uint32{flag_no_active_voucher}, @@ -1970,8 +1907,8 @@ func TestSetDefaultVoucher(t *testing.T) { mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) - if len(tt.vouchersResp.Result.Holdings) > 0 { - firstVoucher := tt.vouchersResp.Result.Holdings[0] + if len(tt.vouchersResp) > 0 { + firstVoucher := tt.vouchersResp[0] mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(firstVoucher.TokenSymbol)).Return(nil) mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(firstVoucher.Balance)).Return(nil) } @@ -2006,8 +1943,7 @@ func TestCheckVouchers(t *testing.T) { mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockVouchersResponse := &models.VoucherHoldingResponse{} - mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ + mockVouchersResponse := []dataserviceapi.TokenHoldings{ {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } From dc198215b1272f5f745babe3e3319295572fac3c Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 02:03:29 +0000 Subject: [PATCH 050/332] Remove commented code --- internal/handlers/ussd/menuhandler.go | 1 - internal/handlers/ussd/menuhandler_test.go | 1 - internal/models/balanceresponse.go | 8 -------- internal/models/trackstatusresponse.go | 7 ------- internal/models/vouchersresponse.go | 1 - remote/accountservice.go | 16 ---------------- 6 files changed, 34 deletions(-) delete mode 100644 internal/models/vouchersresponse.go diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5f6f81a..98a633c 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -705,7 +705,6 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input } res.FlagReset = append(res.FlagReset, flag_api_error) - //balance := balanceResponse.Result.Balance balance := balanceResponse.Balance switch balanceType { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 77447ca..8708145 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -815,7 +815,6 @@ func TestAuthorize(t *testing.T) { // Create required mocks mockDataStore := new(mocks.MockUserDataStore) mockCreateAccountService := new(mocks.MockAccountService) - //expectedResult := resource.Result{} mockState := state.NewState(16) flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") diff --git a/internal/models/balanceresponse.go b/internal/models/balanceresponse.go index 3d57c95..b2baa41 100644 --- a/internal/models/balanceresponse.go +++ b/internal/models/balanceresponse.go @@ -3,14 +3,6 @@ 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"` -//} -// type BalanceResult struct { Balance string `json:"balance"` Nonce json.Number `json:"nonce"` diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 6130c6e..202b69b 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -4,13 +4,6 @@ import ( "encoding/json" "time" ) -//type Transaction struct { -// CreatedAt time.Time `json:"createdAt"` -// Status string `json:"status"` -// TransferValue json.Number `json:"transferValue"` -// TxHash string `json:"txHash"` -// TxType string `json:"txType"` -//} type TrackStatusResult struct { CreatedAt time.Time `json:"createdAt"` diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go deleted file mode 100644 index 2640e7f..0000000 --- a/internal/models/vouchersresponse.go +++ /dev/null @@ -1 +0,0 @@ -package models diff --git a/remote/accountservice.go b/remote/accountservice.go index c11910f..bc98cf1 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -18,15 +18,12 @@ var ( ) type AccountServiceInterface interface { - //CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResponse, error) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) CreateAccount(ctx context.Context) (*models.AccountResult, error) - //CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResponse, error) // TODO: poorly named method seems to be checking transaction CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) - //FetchVouchers(ctx context.Context, publicKey string) (*api.OKResponse, error) } type AccountService struct { @@ -169,19 +166,6 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( } return r, nil - -// -// file, err := os.Open("sample_tokens.json") -// if err != nil { -// return nil, err -// } -// defer file.Close() -// var holdings models.VoucherHoldingResponse -// -// if err := json.NewDecoder(file).Decode(&holdings); err != nil { -// return nil, err -// } -// return &holdings, nil } func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { From c4078c528005012a6f3053ff55a6e7cbbc108c0b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 09:21:46 +0300 Subject: [PATCH 051/332] remove extra spaces --- services/registration/confirm_others_new_pin | 2 +- services/registration/pin_reset_success.vis | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/registration/confirm_others_new_pin b/services/registration/confirm_others_new_pin index 46c3275..d345a0a 100644 --- a/services/registration/confirm_others_new_pin +++ b/services/registration/confirm_others_new_pin @@ -1 +1 @@ -Please confirm new PIN for: {{.retrieve_blocked_number}} \ No newline at end of file +Please confirm new PIN for:{{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/pin_reset_success.vis b/services/registration/pin_reset_success.vis index c942519..96dee73 100644 --- a/services/registration/pin_reset_success.vis +++ b/services/registration/pin_reset_success.vis @@ -6,5 +6,3 @@ MOUT quit 9 HALT INCMP main 0 INCMP quit 9 - - From 767a3cd64c700b59c1a54b42c57df7c0a1dd6c2b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 09:38:51 +0300 Subject: [PATCH 052/332] remove pin guard menu option --- services/registration/guard_pin_menu | 1 - services/registration/guard_pin_menu_swa | 1 - 2 files changed, 2 deletions(-) delete mode 100644 services/registration/guard_pin_menu delete mode 100644 services/registration/guard_pin_menu_swa diff --git a/services/registration/guard_pin_menu b/services/registration/guard_pin_menu deleted file mode 100644 index 9de0ba0..0000000 --- a/services/registration/guard_pin_menu +++ /dev/null @@ -1 +0,0 @@ -Guard my PIN \ No newline at end of file diff --git a/services/registration/guard_pin_menu_swa b/services/registration/guard_pin_menu_swa deleted file mode 100644 index c6b126f..0000000 --- a/services/registration/guard_pin_menu_swa +++ /dev/null @@ -1 +0,0 @@ -Linda PIN yangu \ No newline at end of file From bab3f673ebb028d4ad2c174aa7f86a6721b80067 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 14:13:21 +0300 Subject: [PATCH 053/332] Removed unused model --- internal/models/tokenresponse.go | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 internal/models/tokenresponse.go diff --git a/internal/models/tokenresponse.go b/internal/models/tokenresponse.go deleted file mode 100644 index d243d93..0000000 --- a/internal/models/tokenresponse.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -type ApiResponse struct { - OK bool `json:"ok"` - Description string `json:"description"` - Result Result `json:"result"` -} - -type Result struct { - Holdings []Holding `json:"holdings"` -} - -type Holding struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` -} From b9c56b04ce3f1eee05cfcc20a55eb38ee769a4ab Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 11:43:27 +0000 Subject: [PATCH 054/332] Remove obsolete track account status code --- internal/handlers/ussd/menuhandler_test.go | 17 ++++------------- internal/models/trackstatusresponse.go | 6 +++++- .../testutil/testservice/TestAccountService.go | 7 +------ 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 8708145..8c3867a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -7,7 +7,6 @@ import ( "log" "path" "testing" - "time" "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" @@ -1064,11 +1063,7 @@ func TestCheckAccountStatus(t *testing.T) { }, }, response: &models.TrackStatusResult { - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: true, }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, @@ -1079,11 +1074,7 @@ func TestCheckAccountStatus(t *testing.T) { name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), response: &models.TrackStatusResult{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: false, }, serverResponse: &api.OKResponse{ Ok: true, @@ -1109,13 +1100,13 @@ func TestCheckAccountStatus(t *testing.T) { flagManager: fm.parser, } - status := tt.response.Status + status := tt.response.Active // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() // Call the method under test res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) diff --git a/internal/models/trackstatusresponse.go b/internal/models/trackstatusresponse.go index 202b69b..47d757d 100644 --- a/internal/models/trackstatusresponse.go +++ b/internal/models/trackstatusresponse.go @@ -5,10 +5,14 @@ import ( "time" ) -type TrackStatusResult struct { +type Transaction struct { CreatedAt time.Time `json:"createdAt"` Status string `json:"status"` TransferValue json.Number `json:"transferValue"` TxHash string `json:"txHash"` TxType string `json:"txType"` } + +type TrackStatusResult struct { + Active bool `json:"active"` +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 885e63f..78a2798 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -3,7 +3,6 @@ package testservice import ( "context" "encoding/json" - "time" "git.grassecon.net/urdt/ussd/internal/models" "github.com/grassrootseconomics/eth-custodial/pkg/api" @@ -30,11 +29,7 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { return &models.TrackStatusResult { - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", + Active: true, }, nil } From b2d180e8ebc0d353a6939d78e8f965fa6b405b89 Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 12:15:07 +0000 Subject: [PATCH 055/332] WIP Trying to clean up account status check --- internal/handlers/ussd/menuhandler.go | 5 +- internal/handlers/ussd/menuhandler_test.go | 35 +++--------- internal/testutil/mocks/servicemock.go | 8 +-- .../testservice/TestAccountService.go | 13 +---- remote/accountservice.go | 55 +------------------ 5 files changed, 15 insertions(+), 101 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 98a633c..ed5f466 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -557,17 +557,16 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b if err != nil { return res, err } - okResponse, err = h.accountService.TrackAccountStatus(ctx, string(publicKey)) + r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) - isActive := okResponse.Result["active"].(bool) if !ok { return res, err } - if isActive { + if r.Active { res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagReset = append(res.FlagReset, flag_account_pending) } else { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 8c3867a..7940704 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1053,36 +1053,18 @@ func TestCheckAccountStatus(t *testing.T) { expectedResult resource.Result }{ { - name: "Test when account is on the Sarafu network", - input: []byte("TrackingId1234"), - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, - response: &models.TrackStatusResult { - Active: true, - }, - expectedResult: resource.Result{ - FlagSet: []uint32{flag_account_success}, - FlagReset: []uint32{flag_api_error, flag_account_pending}, - }, - }, - { - name: "Test when the account is not yet on the sarafu network", + name: "Test when the account is not yet on the sarafu network", input: []byte("TrackingId1234"), response: &models.TrackStatusResult{ Active: false, }, - serverResponse: &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": false, - }, - }, +// serverResponse: &api.OKResponse{ +// Ok: true, +// Description: "Account creation succeeded", +// Result: map[string]any{ +// "active": false, +// }, +// }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_pending}, FlagReset: []uint32{flag_api_error, flag_account_success}, @@ -1104,7 +1086,6 @@ func TestCheckAccountStatus(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) - mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 3b36ba7..bec48fd 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -4,7 +4,6 @@ import ( "context" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/stretchr/testify/mock" ) @@ -24,13 +23,8 @@ func (m *MockAccountService) CheckBalance(ctx context.Context, publicKey string) return args.Get(0).(*models.BalanceResult), args.Error(1) } -func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*api.OKResponse, error) { +func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { args := m.Called(trackingId) - return args.Get(0).(*api.OKResponse), args.Error(1) -} - -func (m *MockAccountService) CheckAccountStatus(ctx context.Context,publicKey string) (*models.TrackStatusResult, error) { - args := m.Called(publicKey) return args.Get(0).(*models.TrackStatusResult), args.Error(1) } diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 78a2798..a9a3085 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -5,7 +5,6 @@ import ( "encoding/json" "git.grassecon.net/urdt/ussd/internal/models" - "github.com/grassrootseconomics/eth-custodial/pkg/api" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -27,22 +26,12 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin return balanceResponse, nil } -func (tas *TestAccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { return &models.TrackStatusResult { Active: true, }, nil } -func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { - return &api.OKResponse{ - Ok: true, - Description: "Account creation succeeded", - Result: map[string]any{ - "active": true, - }, - }, nil -} - func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { return []dataserviceapi.TokenHoldings { dataserviceapi.TokenHoldings { diff --git a/remote/accountservice.go b/remote/accountservice.go index bc98cf1..24a469e 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -20,9 +20,7 @@ var ( type AccountServiceInterface interface { CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) CreateAccount(ctx context.Context) (*models.AccountResult, error) - // TODO: poorly named method seems to be checking transaction - CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) - TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) + TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) } @@ -38,7 +36,7 @@ type AccountService struct { // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil -func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (as *AccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { var r models.TrackStatusResult ep, err := url.JoinPath(config.TrackURL, trackingId) @@ -59,60 +57,13 @@ func (as *AccountService) CheckAccountStatus(ctx context.Context, trackingId str return &r, nil } -func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*api.OKResponse, error) { - var okResponse api.OKResponse - var errResponse api.ErrResponse - var err error - - ep, err := url.JoinPath(config.TrackURL, publicKey) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("GET", ep, nil) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("X-GE-KEY", "xd") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - errResponse.Description = err.Error() - return nil, err - } - if resp.StatusCode >= http.StatusBadRequest { - err := json.Unmarshal([]byte(body), &errResponse) - if err != nil { - return nil, err - } - return nil, errors.New(errResponse.Description) - } - err = json.Unmarshal([]byte(body), &okResponse) - if err != nil { - return nil, err - } - if len(okResponse.Result) == 0 { - return nil, errors.New("Empty api result") - } - return &okResponse, nil - -} - // CheckBalance retrieves the balance for a given public key from the custodial balance API endpoint. // Parameters: // - publicKey: The public key associated with the account whose balance needs to be checked. func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { var balanceResult models.BalanceResult - ep, err := url.JoinPath(config.BalanceURL, publicKey) + ep, err := url.JoinPath(config.BalanceURL, publicKey) if err != nil { return nil, err } From 3ae75b27a5cfa10726f2dcf3b2a7afe5a7807cfe Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 12:38:31 +0000 Subject: [PATCH 056/332] WIP replaced check account method but traversal crashes --- internal/handlers/ussd/menuhandler_test.go | 7 ++++--- remote/accountservice.go | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 7940704..6105657 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -20,7 +20,7 @@ import ( "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" - "github.com/grassrootseconomics/eth-custodial/pkg/api" +// "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" @@ -1048,7 +1048,7 @@ func TestCheckAccountStatus(t *testing.T) { tests := []struct { name string input []byte - serverResponse *api.OKResponse + //serverResponse *api.OKResponse response *models.TrackStatusResult expectedResult resource.Result }{ @@ -1086,7 +1086,8 @@ func TestCheckAccountStatus(t *testing.T) { // Define expected interactions with the mock mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_PUBLIC_KEY).Return(tt.input, nil) - mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) + //mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) + mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.response, nil) mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_STATUS, status).Return(nil).Maybe() // Call the method under test diff --git a/remote/accountservice.go b/remote/accountservice.go index 24a469e..bc6a448 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -36,10 +36,10 @@ type AccountService struct { // - string: The status of the transaction as a string. If there is an error during the request or processing, this will be an empty string. // - error: An error if any occurred during the HTTP request, reading the response, or unmarshalling the JSON data. // If no error occurs, this will be nil -func (as *AccountService) TrackAccountStatus(ctx context.Context, trackingId string) (*models.TrackStatusResult, error) { +func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { var r models.TrackStatusResult - ep, err := url.JoinPath(config.TrackURL, trackingId) + ep, err := url.JoinPath(config.TrackURL, publicKey) if err != nil { return nil, err } From 4bf56c525f5b0e9ac90e2c22fc68805327d03108 Mon Sep 17 00:00:00 2001 From: lash Date: Thu, 31 Oct 2024 13:19:44 +0000 Subject: [PATCH 057/332] Export voucher related code --- {internal/utils => common}/vouchers.go | 45 ++++++++++----------- {internal/utils => common}/vouchers_test.go | 2 +- internal/handlers/ussd/menuhandler.go | 10 ++--- 3 files changed, 28 insertions(+), 29 deletions(-) rename {internal/utils => common}/vouchers.go (74%) rename {internal/utils => common}/vouchers_test.go (99%) diff --git a/internal/utils/vouchers.go b/common/vouchers.go similarity index 74% rename from internal/utils/vouchers.go rename to common/vouchers.go index 0372700..291f28d 100644 --- a/internal/utils/vouchers.go +++ b/common/vouchers.go @@ -1,4 +1,4 @@ -package utils +package common import ( "context" @@ -6,7 +6,6 @@ import ( "strings" "git.grassecon.net/urdt/ussd/internal/storage" - "git.grassecon.net/urdt/ussd/common" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -96,13 +95,13 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, return } -// StoreTemporaryVoucher saves voucher metadata as temporary entries in the common.DataStore. -func StoreTemporaryVoucher(ctx context.Context, store common.DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { - entries := map[common.DataTyp][]byte{ - common.DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), - common.DATA_TEMPORARY_BAL: []byte(data.Balance), - common.DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), - common.DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), +// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore. +func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { + entries := map[DataTyp][]byte{ + DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), + DATA_TEMPORARY_BAL: []byte(data.Balance), + DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), + DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), } for key, value := range entries { @@ -113,13 +112,13 @@ func StoreTemporaryVoucher(ctx context.Context, store common.DataStore, sessionI return nil } -// GetTemporaryVoucherData retrieves temporary voucher metadata from the common.DataStore. -func GetTemporaryVoucherData(ctx context.Context, store common.DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { - keys := []common.DataTyp{ - common.DATA_TEMPORARY_SYM, - common.DATA_TEMPORARY_BAL, - common.DATA_TEMPORARY_DECIMAL, - common.DATA_TEMPORARY_ADDRESS, +// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore. +func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { + keys := []DataTyp{ + DATA_TEMPORARY_SYM, + DATA_TEMPORARY_BAL, + DATA_TEMPORARY_DECIMAL, + DATA_TEMPORARY_ADDRESS, } data := &dataserviceapi.TokenHoldings{} @@ -141,14 +140,14 @@ func GetTemporaryVoucherData(ctx context.Context, store common.DataStore, sessio return data, nil } -// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the common.DataStore. -func UpdateVoucherData(ctx context.Context, store common.DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { +// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. +func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { // Active voucher data entries - activeEntries := map[common.DataTyp][]byte{ - common.DATA_ACTIVE_SYM: []byte(data.TokenSymbol), - common.DATA_ACTIVE_BAL: []byte(data.Balance), - common.DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals), - common.DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(data.TokenSymbol), + DATA_ACTIVE_BAL: []byte(data.Balance), + DATA_ACTIVE_DECIMAL: []byte(data.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(data.ContractAddress), } // Write active data diff --git a/internal/utils/vouchers_test.go b/common/vouchers_test.go similarity index 99% rename from internal/utils/vouchers_test.go rename to common/vouchers_test.go index c59037e..401d4d7 100644 --- a/internal/utils/vouchers_test.go +++ b/common/vouchers_test.go @@ -1,4 +1,4 @@ -package utils +package common import ( "context" diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ed5f466..2e075d7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1117,7 +1117,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } - data := utils.ProcessVouchers(vouchersResp) + data := common.ProcessVouchers(vouchersResp) // Store all voucher data dataMap := map[string]string{ @@ -1167,7 +1167,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr) + metadata, err := common.GetVoucherData(ctx, h.prefixDb, inputStr) if err != nil { return res, fmt.Errorf("failed to retrieve voucher data: %v", err) } @@ -1177,7 +1177,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } - if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { + if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { return res, err } @@ -1198,13 +1198,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re // Get temporary data - tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) + tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { return res, err } // Set as active and clear temporary data - if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { + if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { return res, err } From 0b4bf58107cdbcc1c826eb3d8f0e63073335c0f1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 31 Oct 2024 16:31:29 +0300 Subject: [PATCH 058/332] resolved failing tests due to tempData --- internal/handlers/ussd/menuhandler_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index a960cfc..6672ab1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2119,14 +2119,6 @@ func TestSetVoucher(t *testing.T) { utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress), } - // Define the temporary entries to be cleared - tempEntries := map[utils.DataTyp][]byte{ - utils.DATA_TEMPORARY_SYM: []byte(""), - utils.DATA_TEMPORARY_BAL: []byte(""), - utils.DATA_TEMPORARY_DECIMAL: []byte(""), - utils.DATA_TEMPORARY_ADDRESS: []byte(""), - } - // Mocking ReadEntry calls for temporary data retrieval mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.TokenSymbol), nil) mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) @@ -2138,11 +2130,6 @@ func TestSetVoucher(t *testing.T) { mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) } - // Mocking WriteEntry calls for clearing temporary data - for key, value := range tempEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - h := &Handlers{ userdataStore: mockDataStore, } From ac0b4b2ed1de7ab609441b67feaba1c5a58fcafe Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:08:30 +0300 Subject: [PATCH 059/332] pass context --- internal/testutil/TestEngine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testutil/TestEngine.go b/internal/testutil/TestEngine.go index bd6bc7f..26b889e 100644 --- a/internal/testutil/TestEngine.go +++ b/internal/testutil/TestEngine.go @@ -73,7 +73,7 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userDataStore) lhs.SetPersister(pe) From 12825ae08a268db47ce105271c41807d1130913a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:10:46 +0300 Subject: [PATCH 060/332] setup adminstore in the local handler service --- internal/handlers/handlerservice.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 247e6c5..5990dc8 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -1,6 +1,8 @@ package handlers import ( + "context" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/engine" @@ -34,16 +36,21 @@ type LocalHandlerService struct { Rs resource.Resource } -func NewLocalHandlerService(fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) { +func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) { parser, err := getParser(fp, debug) if err != nil { return nil, err } + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + if err != nil { + return nil, err + } return &LocalHandlerService{ - Parser: parser, - DbRs: dbResource, - Cfg: cfg, - Rs: rs, + Parser: parser, + DbRs: dbResource, + AdminStore: adminstore, + Cfg: cfg, + Rs: rs, }, nil } From 5abe9b78cc66181271cc4fdacfd305df558474b3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:11:26 +0300 Subject: [PATCH 061/332] attach an admin store for the phone numbers --- cmd/africastalking/main.go | 8 +++++++- cmd/async/main.go | 9 ++++++++- cmd/http/main.go | 9 ++++++++- cmd/main.go | 13 +++++++------ 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 79fb091..ad56467 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -131,7 +131,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -139,6 +139,12 @@ func main() { os.Exit(1) } + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/async/main.go b/cmd/async/main.go index 902dfc7..bdb17a6 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -104,8 +104,15 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) + + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) diff --git a/cmd/http/main.go b/cmd/http/main.go index ece882e..39e311e 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -92,13 +92,20 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } + + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index 7dd3cd6..95b84c9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -15,7 +15,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/storage" - "git.grassecon.net/urdt/ussd/internal/utils" ) var ( @@ -49,9 +48,6 @@ func main() { ctx = context.WithValue(ctx, "Database", database) pfp := path.Join(scriptDir, "pp.csv") - as, _ := utils.NewAdminStore(ctx, "admin_numbers") - as.Seed() - cfg := engine.Config{ Root: "root", SessionId: sessionId, @@ -92,9 +88,8 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdatastore) - lhs.SetAdminStore(as) lhs.SetPersister(pe) if err != nil { @@ -102,6 +97,12 @@ func main() { os.Exit(1) } + err = lhs.AdminStore.Seed() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { From b2655b7f117fa5e0c34d5be2e474d902d31df596 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:59:11 +0300 Subject: [PATCH 062/332] remove seed from executable --- cmd/africastalking/main.go | 8 +------- cmd/async/main.go | 6 ------ cmd/http/main.go | 8 +------- cmd/main.go | 6 ------ 4 files changed, 2 insertions(+), 26 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index ad56467..891301a 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -131,7 +131,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -139,12 +139,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/async/main.go b/cmd/async/main.go index bdb17a6..df11dac 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -107,12 +107,6 @@ func main() { lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) diff --git a/cmd/http/main.go b/cmd/http/main.go index 39e311e..df37878 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -92,7 +92,7 @@ func main() { os.Exit(1) } - lhs, err := handlers.NewLocalHandlerService(ctx,pfp, true, dbResource, cfg, rs) + lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) lhs.SetDataStore(&userdataStore) if err != nil { @@ -100,12 +100,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { diff --git a/cmd/main.go b/cmd/main.go index 95b84c9..848068f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -97,12 +97,6 @@ func main() { os.Exit(1) } - err = lhs.AdminStore.Seed() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - accountService := server.AccountService{} hl, err := lhs.GetHandler(&accountService) if err != nil { From 299534ccf1701bf2cbddc7cb68ac34d846745e33 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 20:59:51 +0300 Subject: [PATCH 063/332] define seed as a command in the devtool --- devtools/commands/seed.go | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 devtools/commands/seed.go diff --git a/devtools/commands/seed.go b/devtools/commands/seed.go new file mode 100644 index 0000000..8abd50f --- /dev/null +++ b/devtools/commands/seed.go @@ -0,0 +1,47 @@ +package commands + +import ( + "context" + "encoding/json" + "os" + + "git.defalsify.org/vise.git/logging" + "git.grassecon.net/urdt/ussd/internal/utils" +) + +var ( + logg = logging.NewVanilla().WithDomain("adminstore") +) + +type Admin struct { + PhoneNumber string `json:"phonenumber"` +} + +type Config struct { + Admins []Admin `json:"admins"` +} + +func Seed(ctx context.Context) error { + var config Config + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + store := adminstore.FsStore + if err != nil { + return err + } + defer store.Close() + data, err := os.ReadFile("admin_numbers.json") + if err != nil { + return err + } + if err := json.Unmarshal(data, &config); err != nil { + return err + } + for _, admin := range config.Admins { + err := store.Put(ctx, []byte(admin.PhoneNumber), []byte("1")) + if err != nil { + logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) + return err + } + } + return nil +} From 7aab3cff8ca8dda7feafb2fba15d71bf601f8894 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:00:16 +0300 Subject: [PATCH 064/332] remove seed --- internal/utils/adminstore.go | 40 +++--------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/internal/utils/adminstore.go b/internal/utils/adminstore.go index 43a3bfd..a492479 100644 --- a/internal/utils/adminstore.go +++ b/internal/utils/adminstore.go @@ -2,8 +2,6 @@ package utils import ( "context" - "encoding/json" - "os" "git.defalsify.org/vise.git/db" fsdb "git.defalsify.org/vise.git/db/fs" @@ -14,17 +12,9 @@ var ( logg = logging.NewVanilla().WithDomain("adminstore") ) -type Admin struct { - PhoneNumber string `json:"phonenumber"` -} - -type Config struct { - Admins []Admin `json:"admins"` -} - type AdminStore struct { ctx context.Context - fsStore db.Db + FsStore db.Db } func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { @@ -32,7 +22,7 @@ func NewAdminStore(ctx context.Context, fileName string) (*AdminStore, error) { if err != nil { return nil, err } - return &AdminStore{ctx: ctx, fsStore: fsStore}, nil + return &AdminStore{ctx: ctx, FsStore: fsStore}, nil } func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { @@ -45,33 +35,9 @@ func getFsStore(ctx context.Context, connectStr string) (db.Db, error) { return fsStore, nil } -// Seed initializes a list of phonenumbers with admin privileges -func (as *AdminStore) Seed() error { - var config Config - - store := as.fsStore - defer store.Close() - - data, err := os.ReadFile("admin_numbers.json") - if err != nil { - return err - } - if err := json.Unmarshal(data, &config); err != nil { - return err - } - for _, admin := range config.Admins { - err := store.Put(as.ctx, []byte(admin.PhoneNumber), []byte("1")) - if err != nil { - logg.Printf(logging.LVL_DEBUG, "Failed to insert admin number", admin.PhoneNumber) - return err - } - } - return nil -} - // Checks if the given sessionId is listed as an admin. func (as *AdminStore) IsAdmin(sessionId string) (bool, error) { - _, err := as.fsStore.Get(as.ctx, []byte(sessionId)) + _, err := as.FsStore.Get(as.ctx, []byte(sessionId)) if err != nil { if db.IsNotFound(err) { logg.Printf(logging.LVL_INFO, "Returning false because session id was not found") From 7fa38340ddf3cfaab0207506d048db32bf82dc52 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:00:41 +0300 Subject: [PATCH 065/332] add command to initialize a list of admin numbers --- devtools/main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 devtools/main.go diff --git a/devtools/main.go b/devtools/main.go new file mode 100644 index 0000000..d0d4632 --- /dev/null +++ b/devtools/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "context" + "log" + + "git.grassecon.net/urdt/ussd/devtools/commands" +) + +func main() { + ctx := context.Background() + err := commands.Seed(ctx) + if err != nil { + log.Fatalf("Failed to initialize a list of admins with error %s", err) + } + +} From 53fa6f64ce209bac95129a1815f1edcd01ff369e Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:01:01 +0300 Subject: [PATCH 066/332] define structure of json --- devtools/admin_numbers.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 devtools/admin_numbers.json diff --git a/devtools/admin_numbers.json b/devtools/admin_numbers.json new file mode 100644 index 0000000..ca58a23 --- /dev/null +++ b/devtools/admin_numbers.json @@ -0,0 +1,7 @@ +{ + "admins": [ + { + "phonenumber" : "" + } + ] +} \ No newline at end of file From 7d1a04f089547c90b04d328a99298cb2bc24de99 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 31 Oct 2024 21:01:24 +0300 Subject: [PATCH 067/332] remove from root --- admin_numbers.json | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 admin_numbers.json diff --git a/admin_numbers.json b/admin_numbers.json deleted file mode 100644 index ca58a23..0000000 --- a/admin_numbers.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "admins": [ - { - "phonenumber" : "" - } - ] -} \ No newline at end of file From f4ca4454ea2f61aa798f13253b011dcd414a94ab Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 00:42:23 +0300 Subject: [PATCH 068/332] use a single DATA_TEMPORARY_VALUE for the PIN and voucher data --- internal/handlers/ussd/menuhandler.go | 8 ++-- internal/handlers/ussd/menuhandler_test.go | 26 +++++-------- internal/utils/db.go | 6 +-- internal/utils/vouchers.go | 43 +++++++--------------- internal/utils/vouchers_test.go | 17 +++------ 5 files changed, 34 insertions(+), 66 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dd3efdb..aaa1530 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -222,7 +222,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) ( return res, nil } -// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_PIN +// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE // during the account creation process // and during the change PIN process func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { @@ -247,7 +247,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { return res, err } @@ -264,7 +264,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -295,7 +295,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6672ab1..bf29926 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,8 +19,8 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" - "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" @@ -271,7 +271,7 @@ func TestSaveTemporaryPin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -1013,7 +1013,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_PIN + typ := utils.DATA_TEMPORARY_VALUE for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1806,7 +1806,7 @@ func TestConfirmPin(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -2078,16 +2078,9 @@ func TestViewVoucher(t *testing.T) { } // Set up expectations for mockDataStore - expectedData := map[utils.DataTyp]string{ - utils.DATA_TEMPORARY_SYM: "SRF", - utils.DATA_TEMPORARY_BAL: "100", - utils.DATA_TEMPORARY_DECIMAL: "6", - utils.DATA_TEMPORARY_ADDRESS: "0xd4c288865Ce", - } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - for dataType, dataValue := range expectedData { - mockDataStore.On("WriteEntry", ctx, sessionId, dataType, []byte(dataValue)).Return(nil) - } + mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) @@ -2111,6 +2104,8 @@ func TestSetVoucher(t *testing.T) { ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + // Define the expected active entries activeEntries := map[utils.DataTyp][]byte{ utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol), @@ -2120,10 +2115,7 @@ func TestSetVoucher(t *testing.T) { } // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_SYM).Return([]byte(tempData.TokenSymbol), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_BAL).Return([]byte(tempData.Balance), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_DECIMAL).Return([]byte(tempData.TokenDecimals), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_ADDRESS).Return([]byte(tempData.ContractAddress), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) // Mocking WriteEntry calls for setting active data for key, value := range activeEntries { diff --git a/internal/utils/db.go b/internal/utils/db.go index 802e092..df96dfa 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -22,16 +22,12 @@ const ( DATA_OFFERINGS DATA_RECIPIENT DATA_AMOUNT - DATA_TEMPORARY_PIN + DATA_TEMPORARY_VALUE DATA_VOUCHER_LIST - DATA_TEMPORARY_SYM DATA_ACTIVE_SYM - DATA_TEMPORARY_BAL DATA_ACTIVE_BAL DATA_PUBLIC_KEY_REVERSE - DATA_TEMPORARY_DECIMAL DATA_ACTIVE_DECIMAL - DATA_TEMPORARY_ADDRESS DATA_ACTIVE_ADDRESS ) diff --git a/internal/utils/vouchers.go b/internal/utils/vouchers.go index b027cc1..1c20492 100644 --- a/internal/utils/vouchers.go +++ b/internal/utils/vouchers.go @@ -77,7 +77,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) - + if input == parts[0] || strings.EqualFold(input, parts[1]) { symbol = parts[1] if i < len(balList) { @@ -97,45 +97,30 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, // StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore. func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { - entries := map[DataTyp][]byte{ - DATA_TEMPORARY_SYM: []byte(data.TokenSymbol), - DATA_TEMPORARY_BAL: []byte(data.Balance), - DATA_TEMPORARY_DECIMAL: []byte(data.TokenDecimals), - DATA_TEMPORARY_ADDRESS: []byte(data.ContractAddress), + tempData := fmt.Sprintf("%s,%s,%s,%s", data.TokenSymbol, data.Balance, data.TokenDecimals, data.ContractAddress) + + if err := store.WriteEntry(ctx, sessionId, DATA_TEMPORARY_VALUE, []byte(tempData)); err != nil { + return err } - for key, value := range entries { - if err := store.WriteEntry(ctx, sessionId, key, value); err != nil { - return err - } - } return nil } // GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore. func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*dataserviceapi.TokenHoldings, error) { - keys := []DataTyp{ - DATA_TEMPORARY_SYM, - DATA_TEMPORARY_BAL, - DATA_TEMPORARY_DECIMAL, - DATA_TEMPORARY_ADDRESS, + temp_data, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE) + if err != nil { + return nil, err } + values := strings.SplitN(string(temp_data), ",", 4) + data := &dataserviceapi.TokenHoldings{} - values := make([][]byte, len(keys)) - for i, key := range keys { - value, err := store.ReadEntry(ctx, sessionId, key) - if err != nil { - return nil, err - } - values[i] = value - } - - data.TokenSymbol = string(values[0]) - data.Balance = string(values[1]) - data.TokenDecimals = string(values[2]) - data.ContractAddress = string(values[3]) + data.TokenSymbol = values[0] + data.Balance = values[1] + data.TokenDecimals = values[2] + data.ContractAddress = values[3] return data, nil } diff --git a/internal/utils/vouchers_test.go b/internal/utils/vouchers_test.go index a609d27..c2ff4e3 100644 --- a/internal/utils/vouchers_test.go +++ b/internal/utils/vouchers_test.go @@ -2,6 +2,7 @@ package utils import ( "context" + "fmt" "testing" "git.grassecon.net/urdt/ussd/internal/storage" @@ -126,18 +127,12 @@ func TestStoreTemporaryVoucher(t *testing.T) { require.NoError(t, err) // Verify stored data - expectedEntries := map[DataTyp][]byte{ - DATA_TEMPORARY_SYM: []byte("SRF"), - DATA_TEMPORARY_BAL: []byte("200"), - DATA_TEMPORARY_DECIMAL: []byte("6"), - DATA_TEMPORARY_ADDRESS: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), - } + expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + + storedValue, err := store.ReadEntry(ctx, sessionId, DATA_TEMPORARY_VALUE) + require.NoError(t, err) + require.Equal(t, expectedData, string(storedValue), "Mismatch for key %v", DATA_TEMPORARY_VALUE) - for key, expectedValue := range expectedEntries { - storedValue, err := store.ReadEntry(ctx, sessionId, key) - require.NoError(t, err) - require.Equal(t, expectedValue, storedValue, "Mismatch for key %v", key) - } } func TestGetTemporaryVoucherData(t *testing.T) { From 63eed81d3d5ccb0c9433eda7b9fd289463c6d4f0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 00:44:15 +0300 Subject: [PATCH 069/332] Merge branch 'master' into consolidate-temp-data-storage --- internal/handlers/ussd/menuhandler.go | 118 +++++++++++++------- internal/handlers/ussd/menuhandler_test.go | 52 +++++++-- internal/utils/db.go | 7 ++ services/registration/enter_familyname.vis | 2 +- services/registration/enter_location.vis | 2 +- services/registration/enter_name.vis | 2 +- services/registration/enter_offerings.vis | 2 +- services/registration/enter_yob.vis | 4 +- services/registration/set_female.vis | 2 +- services/registration/set_male.vis | 2 +- services/registration/set_unspecified.vis | 2 +- services/registration/update_age | 2 + services/registration/update_familyname.vis | 2 + services/registration/update_firstname.vis | 2 + services/registration/update_gender.vis | 2 + services/registration/update_location.vis | 2 + services/registration/update_offerings.vis | 2 + services/registration/update_yob.vis | 2 + 18 files changed, 150 insertions(+), 59 deletions(-) create mode 100644 services/registration/update_age create mode 100644 services/registration/update_familyname.vis create mode 100644 services/registration/update_firstname.vis create mode 100644 services/registration/update_gender.vis create mode 100644 services/registration/update_location.vis create mode 100644 services/registration/update_offerings.vis create mode 100644 services/registration/update_yob.vis diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index aaa1530..60dd11c 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -19,9 +19,9 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" + "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/utils" - "git.grassecon.net/urdt/ussd/common" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -33,7 +33,6 @@ var ( translationDir = path.Join(scriptDir, "locale") okResponse *api.OKResponse errResponse *api.ErrResponse - backOption = []byte("0") ) // FlagManager handles centralized flag management @@ -334,13 +333,18 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil + firstName := string(input) + store := h.userdataStore + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + if allowUpdate { + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME) + err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) + if err != nil { + return res, err } - firstName := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)) + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)) if err != nil { return res, err } @@ -357,20 +361,24 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil - } - familyName := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)) + store := h.userdataStore + familyName := string(input) + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME) + err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - return res, fmt.Errorf("a family name cannot be less than one character") + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)) + if err != nil { + return res, err + } } - return res, nil } @@ -382,10 +390,19 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou if !ok { return res, fmt.Errorf("missing session") } - if len(input) == 4 { - yob := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(yob)) + yob := string(input) + store := h.userdataStore + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB) + err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) + if err != nil { + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)) if err != nil { return res, err } @@ -402,13 +419,20 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - if bytes.Equal(input, backOption) { - return res, nil + location := string(input) + store := h.userdataStore + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION) + err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) + if err != nil { + return res, err } - location := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(location)) + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(location)) if err != nil { return res, err } @@ -426,14 +450,22 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re if !ok { return res, fmt.Errorf("missing session") } - if bytes.Equal(input, backOption) { - return res, nil - } gender := strings.Split(symbol, "_")[1] store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender)) - if err != nil { - return res, nil + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER) + err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) + if err != nil { + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(gender)) + if err != nil { + return res, err + } } return res, nil @@ -447,12 +479,22 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - if len(input) > 0 { - offerings := string(input) - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)) + offerings := string(input) + store := h.userdataStore + + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + + if allowUpdate { + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS) + err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { - return res, nil + return res, err + } + } else { + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)) + if err != nil { + return res, err } } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index bf29926..f6f1069 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -174,8 +174,11 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { } func TestSaveFirstname(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create new mocks mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -183,11 +186,13 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FIRST_NAME, []byte(firstName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + flagManager: fm.parser, + st: mockState, } // Call the method @@ -204,6 +209,9 @@ func TestSaveFirstname(t *testing.T) { func TestSaveFamilyname(t *testing.T) { // Create a new instance of UserDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -211,11 +219,13 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -288,8 +298,11 @@ func TestSaveTemporaryPin(t *testing.T) { } func TestSaveYoB(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create new instances mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -297,11 +310,13 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_YOB, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -318,6 +333,9 @@ func TestSaveYoB(t *testing.T) { func TestSaveLocation(t *testing.T) { // Create a new instance of MockMyDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -325,11 +343,13 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_LOCATION, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -346,6 +366,9 @@ func TestSaveLocation(t *testing.T) { func TestSaveOfferings(t *testing.T) { // Create a new instance of MockUserDataStore mockStore := new(mocks.MockUserDataStore) + mockState := state.NewState(16) + + fm, err := NewFlagManager(flagsPath) // Define test data sessionId := "session123" @@ -353,11 +376,13 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_OFFERINGS, []byte(offerings)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, + st: mockState, + flagManager: fm.parser, } // Call the method @@ -372,10 +397,12 @@ func TestSaveOfferings(t *testing.T) { } func TestSaveGender(t *testing.T) { - // Create a new instance of MockMyDataStore + // Create a new mock instances mockStore := new(mocks.MockUserDataStore) mockState := state.NewState(16) + fm, _ := NewFlagManager(flagsPath) + // Define the session ID and context sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) @@ -415,16 +442,17 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { - expectedKey := utils.DATA_GENDER + expectedKey := utils.DATA_TEMPORARY_GENDER mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: mockStore, st: mockState, + flagManager: fm.parser, } // Call the method @@ -435,9 +463,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) } }) } diff --git a/internal/utils/db.go b/internal/utils/db.go index df96dfa..4d29c48 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -14,11 +14,17 @@ const ( DATA_CUSTODIAL_ID DATA_ACCOUNT_PIN DATA_ACCOUNT_STATUS + DATA_TEMPORARY_FIRST_NAME DATA_FIRST_NAME + DATA_TEMPORARY_FAMILY_NAME DATA_FAMILY_NAME + DATA_TEMPORARY_YOB DATA_YOB + DATA_TEMPORARY_LOCATION DATA_LOCATION + DATA_TEMPORARY_GENDER DATA_GENDER + DATA_TEMPORARY_OFFERINGS DATA_OFFERINGS DATA_RECIPIENT DATA_AMOUNT @@ -29,6 +35,7 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS + ) func typToBytes(typ DataTyp) []byte { diff --git a/services/registration/enter_familyname.vis b/services/registration/enter_familyname.vis index 5a684ed..5db4c17 100644 --- a/services/registration/enter_familyname.vis +++ b/services/registration/enter_familyname.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_familyname flag_allow_update 1 MOUT back 0 HALT LOAD save_familyname 0 diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index c8da2dd..8966872 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_location flag_allow_update 1 MOUT back 0 HALT LOAD save_location 0 diff --git a/services/registration/enter_name.vis b/services/registration/enter_name.vis index 799b2a1..f853d0a 100644 --- a/services/registration/enter_name.vis +++ b/services/registration/enter_name.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_firstname flag_allow_update 1 MOUT back 0 HALT LOAD save_firstname 0 diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis index 26e4b61..5cc7977 100644 --- a/services/registration/enter_offerings.vis +++ b/services/registration/enter_offerings.vis @@ -1,5 +1,5 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_offerings flag_allow_update 1 LOAD save_offerings 0 MOUT back 0 HALT diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis index 40bf3f4..c74aeed 100644 --- a/services/registration/enter_yob.vis +++ b/services/registration/enter_yob.vis @@ -1,10 +1,10 @@ CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 -LOAD save_yob 0 +CATCH update_yob flag_allow_update 1 MOUT back 0 HALT LOAD verify_yob 0 CATCH incorrect_date_format flag_incorrect_date_format 1 +LOAD save_yob 0 RELOAD save_yob INCMP _ 0 INCMP pin_entry * diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis index 723b080..e211ada 100644 --- a/services/registration/set_female.vis +++ b/services/registration/set_female.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis index 723b080..e211ada 100644 --- a/services/registration/set_male.vis +++ b/services/registration/set_male.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis index 723b080..e211ada 100644 --- a/services/registration/set_unspecified.vis +++ b/services/registration/set_unspecified.vis @@ -1,4 +1,4 @@ LOAD save_gender 0 CATCH incorrect_pin flag_incorrect_pin 1 -CATCH profile_update_success flag_allow_update 1 +CATCH update_gender flag_allow_update 1 MOVE pin_entry diff --git a/services/registration/update_age b/services/registration/update_age new file mode 100644 index 0000000..76ca306 --- /dev/null +++ b/services/registration/update_age @@ -0,0 +1,2 @@ +RELOAD save_yob +CATCH profile_update_success flag_allow_update 1 \ No newline at end of file diff --git a/services/registration/update_familyname.vis b/services/registration/update_familyname.vis new file mode 100644 index 0000000..7cd4d9f --- /dev/null +++ b/services/registration/update_familyname.vis @@ -0,0 +1,2 @@ +RELOAD save_familyname +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_firstname.vis b/services/registration/update_firstname.vis new file mode 100644 index 0000000..dca7036 --- /dev/null +++ b/services/registration/update_firstname.vis @@ -0,0 +1,2 @@ +RELOAD save_firstname +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_gender.vis b/services/registration/update_gender.vis new file mode 100644 index 0000000..506a56a --- /dev/null +++ b/services/registration/update_gender.vis @@ -0,0 +1,2 @@ +RELOAD save_gender +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_location.vis b/services/registration/update_location.vis new file mode 100644 index 0000000..16c4ea2 --- /dev/null +++ b/services/registration/update_location.vis @@ -0,0 +1,2 @@ +RELOAD save_location +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_offerings.vis b/services/registration/update_offerings.vis new file mode 100644 index 0000000..4aeed74 --- /dev/null +++ b/services/registration/update_offerings.vis @@ -0,0 +1,2 @@ +RELOAD save_offerings +CATCH profile_update_success flag_allow_update 1 diff --git a/services/registration/update_yob.vis b/services/registration/update_yob.vis new file mode 100644 index 0000000..a9388ae --- /dev/null +++ b/services/registration/update_yob.vis @@ -0,0 +1,2 @@ +RELOAD save_yob +CATCH profile_update_success flag_allow_update 1 From d7ea8fa65196e1de6fd1c08f4f0a408de1166ba8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 01:10:58 +0300 Subject: [PATCH 070/332] Use the DATA_TEMPORARY_VALUE for the user data --- internal/handlers/ussd/menuhandler.go | 24 +++++++++++----------- internal/handlers/ussd/menuhandler_test.go | 18 ++++++++-------- internal/utils/db.go | 6 ------ 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 60dd11c..5f0c2d9 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -338,13 +338,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME) + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { return res, err } @@ -368,13 +368,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME) + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { return res, err } @@ -396,13 +396,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB) + temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { return res, err } @@ -426,13 +426,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION) + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(location)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { return res, err } @@ -456,13 +456,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER) + temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(gender)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { return res, err } @@ -486,13 +486,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS) + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index f6f1069..77ae68c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -186,7 +186,7 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FIRST_NAME, []byte(firstName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -219,7 +219,7 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_FAMILY_NAME, []byte(familyName)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -310,7 +310,7 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_YOB, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -343,7 +343,7 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_LOCATION, []byte(yob)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -376,7 +376,7 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_OFFERINGS, []byte(offerings)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -442,10 +442,10 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { - expectedKey := utils.DATA_TEMPORARY_GENDER + expectedKey := utils.DATA_TEMPORARY_VALUE mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)).Return(nil) + mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -463,9 +463,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_GENDER, []byte(tt.expectedGender)) + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } }) } diff --git a/internal/utils/db.go b/internal/utils/db.go index 4d29c48..b37240d 100644 --- a/internal/utils/db.go +++ b/internal/utils/db.go @@ -14,17 +14,11 @@ const ( DATA_CUSTODIAL_ID DATA_ACCOUNT_PIN DATA_ACCOUNT_STATUS - DATA_TEMPORARY_FIRST_NAME DATA_FIRST_NAME - DATA_TEMPORARY_FAMILY_NAME DATA_FAMILY_NAME - DATA_TEMPORARY_YOB DATA_YOB - DATA_TEMPORARY_LOCATION DATA_LOCATION - DATA_TEMPORARY_GENDER DATA_GENDER - DATA_TEMPORARY_OFFERINGS DATA_OFFERINGS DATA_RECIPIENT DATA_AMOUNT From 9a528cfd1440f72d0678d318e847e7d10fbdee69 Mon Sep 17 00:00:00 2001 From: lash Date: Fri, 1 Nov 2024 03:17:01 +0000 Subject: [PATCH 071/332] Clean up messily failed conflict resolution --- common/vouchers_test.go | 17 +++-- internal/handlers/ussd/menuhandler.go | 44 ++++++------- internal/handlers/ussd/menuhandler_test.go | 74 ++++------------------ 3 files changed, 44 insertions(+), 91 deletions(-) diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 95fce6e..8b9fa2a 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -9,13 +9,12 @@ import ( "github.com/stretchr/testify/require" "git.grassecon.net/urdt/ussd/internal/storage" - "git.grassecon.net/urdt/ussd/common" memdb "git.defalsify.org/vise.git/db/mem" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) // InitializeTestDb sets up and returns an in-memory database and store. -func InitializeTestDb(t *testing.T) (context.Context, *common.UserDataStore) { +func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { ctx := context.Background() // Initialize memDb @@ -23,8 +22,8 @@ func InitializeTestDb(t *testing.T) (context.Context, *common.UserDataStore) { err := db.Connect(ctx, "") require.NoError(t, err, "Failed to connect to memDb") - // Create common.UserDataStore with memDb - store := &common.UserDataStore{Db: db} + // Create UserDataStore with memDb + store := &UserDataStore{Db: db} t.Cleanup(func() { db.Close() // Ensure the DB is closed after each test @@ -183,11 +182,11 @@ func TestUpdateVoucherData(t *testing.T) { require.NoError(t, err) // Verify active data was stored correctly - activeEntries := map[common.DataTyp][]byte{ - common.DATA_ACTIVE_SYM: []byte(newData.TokenSymbol), - common.DATA_ACTIVE_BAL: []byte(newData.Balance), - common.DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals), - common.DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress), + activeEntries := map[DataTyp][]byte{ + DATA_ACTIVE_SYM: []byte(newData.TokenSymbol), + DATA_ACTIVE_BAL: []byte(newData.Balance), + DATA_ACTIVE_DECIMAL: []byte(newData.TokenDecimals), + DATA_ACTIVE_ADDRESS: []byte(newData.ContractAddress), } for key, expectedValue := range activeEntries { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index db8d520..42785d7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -20,7 +20,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/common" - "git.grassecon.net/urdt/ussd/internal/handlers/server" + "git.grassecon.net/urdt/ussd/remote" "git.grassecon.net/urdt/ussd/internal/utils" "gopkg.in/leonelquinteros/gotext.v1" @@ -246,7 +246,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_incorrect_pin) store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(accountPIN)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { return res, err } @@ -263,7 +263,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -294,7 +294,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, fmt.Errorf("missing session") } store := h.userdataStore - temporaryPin, err := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -338,13 +338,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_FIRST_NAME, []byte(temporaryFirstName)) + temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { return res, err } @@ -369,13 +369,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) + temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { return res, err } @@ -397,13 +397,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_YOB, []byte(temporaryYob)) + temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { return res, err } @@ -427,13 +427,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_LOCATION, []byte(temporaryLocation)) + temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { return res, err } @@ -457,13 +457,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(temporaryGender)) + temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(gender)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { return res, err } @@ -488,13 +488,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) allowUpdate := h.st.MatchFlag(flag_allow_update, true) if allowUpdate { - temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE) - err = store.WriteEntry(ctx, sessionId, utils.DATA_OFFERINGS, []byte(temporaryOfferings)) + temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { return res, err } } else { - err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)) + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { return res, err } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 30f84d9..2eb6519 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -19,7 +19,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/common" - "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" // "github.com/grassrootseconomics/eth-custodial/pkg/api" testdataloader "github.com/peteole/testdata-loader" @@ -181,11 +180,7 @@ func TestSaveFirstname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FIRST_NAME, []byte(firstName)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -218,11 +213,7 @@ func TestSaveFamilyname(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_FAMILY_NAME, []byte(familyName)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -284,11 +275,7 @@ func TestSaveTemporaryPin(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN, []byte(tt.input)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -317,11 +304,7 @@ func TestSaveYoB(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_YOB, []byte(yob)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -354,11 +337,7 @@ func TestSaveLocation(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_LOCATION, []byte(yob)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -391,11 +370,7 @@ func TestSaveOfferings(t *testing.T) { ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Set up the expected behavior of the mock -<<<<<<< HEAD - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_OFFERINGS, []byte(offerings)).Return(nil) -======= - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) // Create the Handlers instance with the mock store h := &Handlers{ @@ -461,17 +436,10 @@ func TestSaveGender(t *testing.T) { t.Run(tt.name, func(t *testing.T) { // Set up expectations for the mock database if tt.expectCall { -<<<<<<< HEAD - expectedKey := common.DATA_GENDER + expectedKey := common.DATA_TEMPORARY_VALUE mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) } else { - mockStore.On("WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)).Return(nil) -======= - expectedKey := utils.DATA_TEMPORARY_VALUE - mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) - } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) ->>>>>>> master + mockStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) } mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) // Create the Handlers instance with the mock store @@ -489,15 +457,9 @@ func TestSaveGender(t *testing.T) { // Verify expectations if tt.expectCall { -<<<<<<< HEAD - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) + mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_GENDER, []byte(tt.expectedGender)) -======= - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) ->>>>>>> master + mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) } }) } @@ -1072,11 +1034,7 @@ func TestVerifyCreatePin(t *testing.T) { }, } -<<<<<<< HEAD - typ := common.DATA_TEMPORARY_PIN -======= - typ := utils.DATA_TEMPORARY_VALUE ->>>>>>> master + typ := common.DATA_TEMPORARY_VALUE for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1816,11 +1774,7 @@ func TestConfirmPin(t *testing.T) { // Set up the expected behavior of the mock mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) -<<<<<<< HEAD - mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_PIN).Return(tt.temporarypin, nil) -======= - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) ->>>>>>> master + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) @@ -2060,7 +2014,7 @@ func TestViewVoucher(t *testing.T) { // Set up expectations for mockDataStore expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) + mockDataStore.On("WriteEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) @@ -2095,7 +2049,7 @@ func TestSetVoucher(t *testing.T) { } // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) + mockDataStore.On("ReadEntry", ctx, sessionId, common.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) // Mocking WriteEntry calls for setting active data for key, value := range activeEntries { From 0014693ba8b3b8f1545e59e3cf66bf6bf71caebb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 06:39:37 +0300 Subject: [PATCH 072/332] remove guard pin option --- menutraversal_test/group_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 8b765f5..a219a6c 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -13,7 +13,7 @@ }, { "input": "5", - "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n3:Guard my PIN\n0:Back" + "expectedContent": "PIN Management\n1:Change PIN\n2:Reset other's PIN\n0:Back" }, { "input": "1", From 7e448f739ab0205b8e3bab16b0081344de573868 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 09:35:48 +0300 Subject: [PATCH 073/332] change fs store path to root --- devtools/commands/seed.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/commands/seed.go b/devtools/commands/seed.go index 8abd50f..e76c83d 100644 --- a/devtools/commands/seed.go +++ b/devtools/commands/seed.go @@ -23,7 +23,7 @@ type Config struct { func Seed(ctx context.Context) error { var config Config - adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + adminstore, err := utils.NewAdminStore(ctx, "../admin_numbers") store := adminstore.FsStore if err != nil { return err From eb2c73dce1478023a34e2d552a649a9c14339d41 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 11:51:50 +0300 Subject: [PATCH 074/332] remove unused setadmin store --- internal/handlers/handlerservice.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 5990dc8..cbc2a29 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -62,10 +62,6 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { ls.UserdataStore = db } -func (ls *LocalHandlerService) SetAdminStore(adminstore *utils.AdminStore) { - ls.AdminStore = adminstore -} - func (ls *LocalHandlerService) GetHandler(accountService server.AccountServiceInterface) (*ussd.Handlers, error) { ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) if err != nil { From cfe3e526dfb1793ab4977998ae730c834ac67d5e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 1 Nov 2024 13:17:45 +0300 Subject: [PATCH 075/332] Remove the subprefixdbmock --- internal/handlers/ussd/menuhandler_test.go | 87 +++++++++++++++------- internal/testutil/mocks/subprefixdbmock.go | 21 ------ 2 files changed, 59 insertions(+), 49 deletions(-) delete mode 100644 internal/testutil/mocks/subprefixdbmock.go diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 77ae68c..6f70108 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -16,6 +16,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" @@ -26,6 +27,7 @@ import ( testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" + memdb "git.defalsify.org/vise.git/db/mem" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -2021,19 +2023,24 @@ func TestSetDefaultVoucher(t *testing.T) { func TestCheckVouchers(t *testing.T) { mockDataStore := new(mocks.MockUserDataStore) mockAccountService := new(mocks.MockAccountService) - mockSubPrefixDb := new(mocks.MockSubPrefixDb) sessionId := "session123" publicKey := "0X13242618721" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) h := &Handlers{ userdataStore: mockDataStore, accountService: mockAccountService, - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) mockVouchersResponse := &models.VoucherHoldingResponse{} @@ -2042,38 +2049,53 @@ func TestCheckVouchers(t *testing.T) { {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } + expectedSym := []byte("1:SRF\n2:MILO") + mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil) - mockSubPrefixDb.On("Put", ctx, []byte("sym"), []byte("1:SRF\n2:MILO")).Return(nil) - mockSubPrefixDb.On("Put", ctx, []byte("bal"), []byte("1:100\n2:200")).Return(nil) - mockSubPrefixDb.On("Put", ctx, []byte("deci"), []byte("1:6\n2:4")).Return(nil) - mockSubPrefixDb.On("Put", ctx, []byte("addr"), []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa")).Return(nil) - - _, err := h.CheckVouchers(ctx, "check_vouchers", []byte("")) - + _, err = h.CheckVouchers(ctx, "check_vouchers", []byte("")) assert.NoError(t, err) + // Read voucher sym data from the store + voucherData, err := spdb.Get(ctx, []byte("sym")) + if err != nil { + t.Fatal(err) + } + + // assert that the data is stored correctly + assert.Equal(t, expectedSym, voucherData) + mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) } func TestGetVoucherList(t *testing.T) { - mockSubPrefixDb := new(mocks.MockSubPrefixDb) - sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + h := &Handlers{ - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } - mockSubPrefixDb.On("Get", ctx, []byte("sym")).Return([]byte("1:SRF\n2:MILO"), nil) + expectedSym := []byte("1:SRF\n2:MILO") + + // Put voucher sym data from the store + err = spdb.Put(ctx, []byte("sym"), expectedSym) + if err != nil { + t.Fatal(err) + } res, err := h.GetVoucherList(ctx, "", []byte("")) - assert.NoError(t, err) - assert.Contains(t, res.Content, "1:SRF\n2:MILO") - mockSubPrefixDb.AssertExpectations(t) + assert.NoError(t, err) + assert.Equal(t, res.Content, string(expectedSym)) } func TestViewVoucher(t *testing.T) { @@ -2082,27 +2104,37 @@ func TestViewVoucher(t *testing.T) { t.Logf(err.Error()) } mockDataStore := new(mocks.MockUserDataStore) - mockSubPrefixDb := new(mocks.MockSubPrefixDb) sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) + db := memdb.NewMemDb() + err = db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + h := &Handlers{ userdataStore: mockDataStore, flagManager: fm.parser, - prefixDb: mockSubPrefixDb, + prefixDb: spdb, } // Define mock voucher data - mockVoucherData := map[string]string{ - "sym": "1:SRF", - "bal": "1:100", - "deci": "1:6", - "addr": "1:0xd4c288865Ce", + mockData := map[string][]byte{ + "sym": []byte("1:SRF\n2:MILO"), + "bal": []byte("1:100\n2:200"), + "deci": []byte("1:6\n2:4"), + "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), } - for key, value := range mockVoucherData { - mockSubPrefixDb.On("Get", ctx, []byte(key)).Return([]byte(value), nil) + // Put the data + for key, value := range mockData { + err = spdb.Put(ctx, []byte(key), []byte(value)) + if err != nil { + t.Fatal(err) + } } // Set up expectations for mockDataStore @@ -2115,7 +2147,6 @@ func TestViewVoucher(t *testing.T) { assert.Contains(t, res.Content, "SRF\n100") mockDataStore.AssertExpectations(t) - mockSubPrefixDb.AssertExpectations(t) } func TestSetVoucher(t *testing.T) { diff --git a/internal/testutil/mocks/subprefixdbmock.go b/internal/testutil/mocks/subprefixdbmock.go deleted file mode 100644 index e98bcb6..0000000 --- a/internal/testutil/mocks/subprefixdbmock.go +++ /dev/null @@ -1,21 +0,0 @@ -package mocks - -import ( - "context" - - "github.com/stretchr/testify/mock" -) - -type MockSubPrefixDb struct { - mock.Mock -} - -func (m *MockSubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { - args := m.Called(ctx, key) - return args.Get(0).([]byte), args.Error(1) -} - -func (m *MockSubPrefixDb) Put(ctx context.Context, key, val []byte) error { - args := m.Called(ctx, key, val) - return args.Error(0) -} From 332074375aee0010d9cbf37f8738bf12e432810d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 16:44:54 +0300 Subject: [PATCH 076/332] wrap in devtools/admin --- devtools/{ => admin}/admin_numbers.json | 0 devtools/{ => admin}/commands/seed.go | 0 devtools/{ => admin}/main.go | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename devtools/{ => admin}/admin_numbers.json (100%) rename devtools/{ => admin}/commands/seed.go (100%) rename devtools/{ => admin}/main.go (79%) diff --git a/devtools/admin_numbers.json b/devtools/admin/admin_numbers.json similarity index 100% rename from devtools/admin_numbers.json rename to devtools/admin/admin_numbers.json diff --git a/devtools/commands/seed.go b/devtools/admin/commands/seed.go similarity index 100% rename from devtools/commands/seed.go rename to devtools/admin/commands/seed.go diff --git a/devtools/main.go b/devtools/admin/main.go similarity index 79% rename from devtools/main.go rename to devtools/admin/main.go index d0d4632..9a527f3 100644 --- a/devtools/main.go +++ b/devtools/admin/main.go @@ -4,7 +4,7 @@ import ( "context" "log" - "git.grassecon.net/urdt/ussd/devtools/commands" + "git.grassecon.net/urdt/ussd/devtools/admin/commands" ) func main() { From 6dbe74d12bc777285b68837fc9c4d0b470559607 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 1 Nov 2024 16:46:09 +0300 Subject: [PATCH 077/332] use single temporary value --- internal/handlers/ussd/menuhandler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 198d45b..feb002e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -237,7 +237,7 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -312,7 +312,7 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { return res, err } @@ -845,7 +845,7 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_PIN) + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE) if err != nil { return res, err } From c52f8312e4641cb684cf9c206b91b89b5617a2ff Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 2 Nov 2024 03:21:38 +0300 Subject: [PATCH 078/332] Remove the dbmock and userdbmock --- internal/testutil/mocks/dbmock.go | 59 --------------------------- internal/testutil/mocks/userdbmock.go | 24 ----------- 2 files changed, 83 deletions(-) delete mode 100644 internal/testutil/mocks/dbmock.go delete mode 100644 internal/testutil/mocks/userdbmock.go diff --git a/internal/testutil/mocks/dbmock.go b/internal/testutil/mocks/dbmock.go deleted file mode 100644 index 0b40eab..0000000 --- a/internal/testutil/mocks/dbmock.go +++ /dev/null @@ -1,59 +0,0 @@ -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) -} diff --git a/internal/testutil/mocks/userdbmock.go b/internal/testutil/mocks/userdbmock.go deleted file mode 100644 index ff3f18d..0000000 --- a/internal/testutil/mocks/userdbmock.go +++ /dev/null @@ -1,24 +0,0 @@ -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) -} From 476a69fe1b7fcd67728a9eaf5f5526b4010d4a1e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 2 Nov 2024 03:22:33 +0300 Subject: [PATCH 079/332] Update menuhandler tests to use the memdb instead of dbmocks --- internal/handlers/ussd/menuhandler_test.go | 1185 +++++++++----------- 1 file changed, 510 insertions(+), 675 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6f70108..48eb81a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -7,10 +7,7 @@ import ( "log" "path" "testing" - "time" - "git.defalsify.org/vise.git/asm" - "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/lang" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" @@ -20,7 +17,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" - "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/internal/utils" "github.com/alecthomas/assert/v2" "github.com/grassrootseconomics/eth-custodial/pkg/api" @@ -36,15 +32,46 @@ var ( flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") ) +// InitializeTestStore sets up and returns an in-memory database and store. +func InitializeTestStore(t *testing.T) (context.Context, *utils.UserDataStore) { + ctx := context.Background() + + // Initialize memDb + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + require.NoError(t, err, "Failed to connect to memDb") + + // Create UserDataStore with memDb + store := &utils.UserDataStore{Db: db} + + t.Cleanup(func() { + db.Close() // Ensure the DB is closed after each test + }) + + return ctx, store +} + +func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storage.SubPrefixDb { + db := memdb.NewMemDb() + err := db.Connect(ctx, "") + if err != nil { + t.Fatal(err) + } + spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + + return spdb +} + func TestNewHandlers(t *testing.T) { + _, store := InitializeTestStore(t) + fm, err := NewFlagManager(flagsPath) accountService := testservice.TestAccountService{} if err != nil { t.Logf(err.Error()) } t.Run("Valid UserDataStore", func(t *testing.T) { - mockStore := &mocks.MockUserDataStore{} - handlers, err := NewHandlers(fm.parser, mockStore, &accountService) + handlers, err := NewHandlers(fm.parser, store, &accountService) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -58,10 +85,7 @@ func TestNewHandlers(t *testing.T) { // Test case for nil userdataStore t.Run("Nil UserDataStore", func(t *testing.T) { - appFlags := &asm.FlagParser{} - - handlers, err := NewHandlers(appFlags, nil, &accountService) - + handlers, err := NewHandlers(fm.parser, nil, &accountService) if err == nil { t.Fatal("expected an error, got none") } @@ -75,19 +99,19 @@ func TestNewHandlers(t *testing.T) { } func TestCreateAccount(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - // Create required mocks + flag_account_created, err := fm.GetFlag("flag_account_created") if err != nil { t.Logf(err.Error()) } - // Define session ID and mock data - sessionId := "session123" - notFoundErr := db.ErrNotFound{} - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { name string @@ -111,47 +135,24 @@ func TestCreateAccount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAccountService := new(mocks.MockAccountService) - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - // Create a Handlers instance with the mock data store h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } - publicKey := tt.serverResponse.Result["publicKey"].(string) - data := map[utils.DataTyp]string{ - utils.DATA_TRACKING_ID: tt.serverResponse.Result["trackingId"].(string), - utils.DATA_PUBLIC_KEY: publicKey, - } - - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACCOUNT_CREATED).Return([]byte(""), notFoundErr) - mockCreateAccountService.On("CreateAccount").Return(tt.serverResponse, nil) - - for key, value := range data { - mockDataStore.On("WriteEntry", ctx, sessionId, key, []byte(value)).Return(nil) - } - publicKeyNormalized, err := common.NormalizeHex(publicKey) - if err != nil { - t.Fatal(err) - } - - mockDataStore.On("WriteEntry", ctx, publicKeyNormalized, utils.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)).Return(nil) + mockAccountService.On("CreateAccount").Return(tt.serverResponse, nil) // Call the method you want to test - res, err := h.CreateAccount(ctx, "create_account", []byte("some-input")) + res, err := h.CreateAccount(ctx, "create_account", []byte("")) // Assert that no errors occurred assert.NoError(t, err) // Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) }) } } @@ -176,23 +177,28 @@ func TestWithPersister_PanicWhenAlreadySet(t *testing.T) { } func TestSaveFirstname(t *testing.T) { - // Create new mocks - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) - fm, err := NewFlagManager(flagsPath) + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) // Define test data - sessionId := "session123" firstName := "John" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)).Return(nil) + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(firstName)); err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, flagManager: fm.parser, st: mockState, } @@ -204,28 +210,34 @@ func TestSaveFirstname(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.Result{}, res) - // Assert all expectations were met - mockStore.AssertExpectations(t) + // Verify that the DATA_FIRST_NAME entry has been updated with the temporary value + storedFirstName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FIRST_NAME) + assert.Equal(t, firstName, string(storedFirstName)) } func TestSaveFamilyname(t *testing.T) { - // Create a new instance of UserDataStore - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) - fm, err := NewFlagManager(flagsPath) + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) // Define test data - sessionId := "session123" familyName := "Doeee" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)).Return(nil) + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(familyName)); err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, st: mockState, flagManager: fm.parser, } @@ -237,25 +249,213 @@ func TestSaveFamilyname(t *testing.T) { assert.NoError(t, err) assert.Equal(t, resource.Result{}, res) - // Assert all expectations were met - mockStore.AssertExpectations(t) + // Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value + storedFamilyName, _ := store.ReadEntry(ctx, sessionId, utils.DATA_FAMILY_NAME) + assert.Equal(t, familyName, string(storedFamilyName)) +} + +func TestSaveYoB(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + yob := "1980" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveYob(ctx, "save_yob", []byte(yob)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_YOB entry has been updated with the temporary value + storedYob, _ := store.ReadEntry(ctx, sessionId, utils.DATA_YOB) + assert.Equal(t, yob, string(storedYob)) +} + +func TestSaveLocation(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + location := "Kilifi" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveLocation(ctx, "save_location", []byte(location)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_LOCATION entry has been updated with the temporary value + storedLocation, _ := store.ReadEntry(ctx, sessionId, utils.DATA_LOCATION) + assert.Equal(t, location, string(storedLocation)) +} + +func TestSaveOfferings(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test data + offerings := "Bananas" + + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)); err != nil { + t.Fatal(err) + } + + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + st: mockState, + } + + // Call the method + res, err := h.SaveOfferings(ctx, "save_offerings", []byte(offerings)) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_OFFERINGS entry has been updated with the temporary value + storedOfferings, _ := store.ReadEntry(ctx, sessionId, utils.DATA_OFFERINGS) + assert.Equal(t, offerings, string(storedOfferings)) +} + +func TestSaveGender(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, _ := NewFlagManager(flagsPath) + + flag_allow_update, _ := fm.GetFlag("flag_allow_update") + + // Set the flag in the State + mockState := state.NewState(16) + mockState.SetFlag(flag_allow_update) + + // Define test cases + tests := []struct { + name string + input []byte + expectedGender string + executingSymbol string + }{ + { + name: "Valid Male Input", + input: []byte("1"), + expectedGender: "male", + executingSymbol: "set_male", + }, + { + name: "Valid Female Input", + input: []byte("2"), + expectedGender: "female", + executingSymbol: "set_female", + }, + { + name: "Valid Unspecified Input", + input: []byte("3"), + executingSymbol: "set_unspecified", + expectedGender: "unspecified", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)); err != nil { + t.Fatal(err) + } + + mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) + // Create the Handlers instance with the mock store + h := &Handlers{ + userdataStore: store, + st: mockState, + flagManager: fm.parser, + } + + // Call the method + res, err := h.SaveGender(ctx, "save_gender", tt.input) + + // Assert results + assert.NoError(t, err) + assert.Equal(t, resource.Result{}, res) + + // Verify that the DATA_GENDER entry has been updated with the temporary value + storedGender, _ := store.ReadEntry(ctx, sessionId, utils.DATA_GENDER) + assert.Equal(t, tt.expectedGender, string(storedGender)) + }) + } } func TestSaveTemporaryPin(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) - mockStore := new(mocks.MockUserDataStore) if err != nil { log.Fatal(err) } + flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, - userdataStore: mockStore, + userdataStore: store, } - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) // Define test cases tests := []struct { @@ -281,10 +481,6 @@ func TestSaveTemporaryPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.input)).Return(nil) - // Call the method res, err := h.SaveTemporaryPin(ctx, "save_pin", tt.input) @@ -293,205 +489,27 @@ func TestSaveTemporaryPin(t *testing.T) { } // Assert that the Result FlagSet has the required flags after language switch - assert.Equal(t, res, tt.expectedResult, "Flags should be equal to account created") - - }) - } -} - -func TestSaveYoB(t *testing.T) { - // Create new instances - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) - - fm, err := NewFlagManager(flagsPath) - - // Define test data - sessionId := "session123" - yob := "1980" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveYob(ctx, "save_yob", []byte(yob)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(t) -} - -func TestSaveLocation(t *testing.T) { - // Create a new instance of MockMyDataStore - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) - - fm, err := NewFlagManager(flagsPath) - - // Define test data - sessionId := "session123" - yob := "Kilifi" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(yob)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveLocation(ctx, "save_location", []byte(yob)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(t) -} - -func TestSaveOfferings(t *testing.T) { - // Create a new instance of MockUserDataStore - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) - - fm, err := NewFlagManager(flagsPath) - - // Define test data - sessionId := "session123" - offerings := "Bananas" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Set up the expected behavior of the mock - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(offerings)).Return(nil) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - res, err := h.SaveOfferings(ctx, "save_offerings", []byte(offerings)) - - // Assert results - assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) - - // Assert all expectations were met - mockStore.AssertExpectations(t) -} - -func TestSaveGender(t *testing.T) { - // Create a new mock instances - mockStore := new(mocks.MockUserDataStore) - mockState := state.NewState(16) - - fm, _ := NewFlagManager(flagsPath) - - // Define the session ID and context - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Define test cases - tests := []struct { - name string - input []byte - expectedGender string - expectCall bool - executingSymbol string - }{ - { - name: "Valid Male Input", - input: []byte("1"), - expectedGender: "male", - executingSymbol: "set_male", - expectCall: true, - }, - { - name: "Valid Female Input", - input: []byte("2"), - expectedGender: "female", - executingSymbol: "set_female", - expectCall: true, - }, - { - name: "Valid Unspecified Input", - input: []byte("3"), - executingSymbol: "set_unspecified", - expectedGender: "unspecified", - expectCall: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Set up expectations for the mock database - if tt.expectCall { - expectedKey := utils.DATA_TEMPORARY_VALUE - mockStore.On("WriteEntry", ctx, sessionId, expectedKey, []byte(tt.expectedGender)).Return(nil) - } else { - mockStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)).Return(nil) - } - mockState.ExecPath = append(mockState.ExecPath, tt.executingSymbol) - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - st: mockState, - flagManager: fm.parser, - } - - // Call the method - _, err := h.SaveGender(ctx, "save_gender", tt.input) - - // Assert no error - assert.NoError(t, err) - - // Verify expectations - if tt.expectCall { - mockStore.AssertCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } else { - mockStore.AssertNotCalled(t, "WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.expectedGender)) - } + assert.Equal(t, res, tt.expectedResult, "Result should match expected result") }) } } func TestCheckIdentifier(t *testing.T) { - // Create a new instance of MockMyDataStore - mockStore := new(mocks.MockUserDataStore) - - // Define the session ID and context sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) // Define test cases tests := []struct { name string - mockPublicKey []byte + publicKey []byte mockErr error expectedContent string expectError bool }{ { name: "Saved public Key", - mockPublicKey: []byte("0xa8363"), + publicKey: []byte("0xa8363"), mockErr: nil, expectedContent: "0xa8363", expectError: false, @@ -500,39 +518,33 @@ func TestCheckIdentifier(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Set up expectations for the mock database - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.mockPublicKey, tt.mockErr) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, } // Call the method res, err := h.CheckIdentifier(ctx, "check_identifier", nil) // Assert results - assert.NoError(t, err) assert.Equal(t, tt.expectedContent, res.Content) - - // Verify expectations - mockStore.AssertExpectations(t) }) } } func TestGetSender(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) + sessionId := "session123" + ctx, _ := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) - // Define test data - sessionId := "254712345678" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - // Create the Handlers instance with the mock store - h := &Handlers{ - userdataStore: mockStore, - } + // Create the Handlers instance + h := &Handlers{} // Call the method res, _ := h.GetSender(ctx, "get_sender", []byte("")) @@ -542,21 +554,27 @@ func TestGetSender(t *testing.T) { } func TestGetAmount(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) // Define test data - sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) amount := "0.03" activeSym := "SRF" - // Set up the expected behavior of the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(activeSym), nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return([]byte(amount), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(amount)) + if err != nil { + t.Fatal(err) + } + + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(activeSym)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, } // Call the method @@ -566,31 +584,30 @@ func TestGetAmount(t *testing.T) { //Assert that the retrieved amount is what was set as the content assert.Equal(t, formattedAmount, res.Content) - } func TestGetRecipient(t *testing.T) { - mockStore := new(mocks.MockUserDataStore) - - // Define test data sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + recepient := "0xcasgatweksalw1018221" - // Set up the expected behavior of the mock - mockStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return([]byte(recepient), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(recepient)) + if err != nil { + t.Fatal(err) + } // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockStore, + userdataStore: store, } // Call the method - res, _ := h.GetRecipient(ctx, "get_recipient", []byte("Getting recipient...")) + res, _ := h.GetRecipient(ctx, "get_recipient", []byte("")) //Assert that the retrieved recepient is what was set as the content assert.Equal(t, recepient, res.Content) - } func TestGetFlag(t *testing.T) { @@ -608,12 +625,11 @@ func TestGetFlag(t *testing.T) { } func TestSetLanguage(t *testing.T) { - // Create a new instance of the Flag Manager fm, err := NewFlagManager(flagsPath) - if err != nil { log.Fatal(err) } + // Define test cases tests := []struct { name string @@ -652,25 +668,24 @@ func TestSetLanguage(t *testing.T) { // Call the method res, err := h.SetLanguage(context.Background(), "set_language", nil) - if err != nil { t.Error(err) } // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should match expected result") - }) } } + func TestResetAllowUpdate(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_allow_update, _ := fm.parser.GetFlag("flag_allow_update") - if err != nil { log.Fatal(err) } + + flag_allow_update, _ := fm.parser.GetFlag("flag_allow_update") + // Define test cases tests := []struct { name string @@ -688,7 +703,6 @@ func TestResetAllowUpdate(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -696,13 +710,12 @@ func TestResetAllowUpdate(t *testing.T) { // Call the method res, err := h.ResetAllowUpdate(context.Background(), "reset_allow update", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Flags should be equal to account created") - }) } } @@ -740,25 +753,24 @@ func TestResetAccountAuthorized(t *testing.T) { // Call the method res, err := h.ResetAccountAuthorized(context.Background(), "reset_account_authorized", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestIncorrectPinReset(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") - if err != nil { log.Fatal(err) } + + flag_incorrect_pin, _ := fm.parser.GetFlag("flag_incorrect_pin") + // Define test cases tests := []struct { name string @@ -776,7 +788,6 @@ func TestIncorrectPinReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -784,25 +795,24 @@ func TestIncorrectPinReset(t *testing.T) { // Call the method res, err := h.ResetIncorrectPin(context.Background(), "reset_incorrect_pin", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestResetIncorrectYob(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") - if err != nil { log.Fatal(err) } + + flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") + // Define test cases tests := []struct { name string @@ -820,7 +830,6 @@ func TestResetIncorrectYob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create the Handlers instance with the mock flag manager h := &Handlers{ flagManager: fm.parser, @@ -828,43 +837,39 @@ func TestResetIncorrectYob(t *testing.T) { // Call the method res, err := h.ResetIncorrectYob(context.Background(), "reset_incorrect_yob", tt.input) - if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestAuthorize(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - //expectedResult := resource.Result{} + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_incorrect_pin, _ := fm.GetFlag("flag_incorrect_pin") flag_account_authorized, _ := fm.GetFlag("flag_account_authorized") flag_allow_update, _ := fm.GetFlag("flag_allow_update") - //Assuming 1234 is the correct account pin + + // Set 1234 is the correct account pin accountPIN := "1234" - // Define session ID and mock data - sessionId := "session123" - typ := utils.DATA_ACCOUNT_PIN - h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -899,14 +904,13 @@ func TestAuthorize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Create context with session ID ctx := context.WithValue(context.Background(), "SessionId", sessionId) - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte(accountPIN), nil) - - // Create a Handlers instance with the mock data store + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(accountPIN)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, err := h.Authorize(ctx, "authorize", []byte(tt.input)) @@ -916,33 +920,25 @@ func TestAuthorize(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } - } func TestVerifyYob(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } sessionId := "session123" // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_incorrect_date_format, _ := fm.parser.GetFlag("flag_incorrect_date_format") ctx := context.WithValue(context.Background(), "SessionId", sessionId) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -977,7 +973,6 @@ func TestVerifyYob(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Call the method under test res, err := h.VerifyYob(ctx, "verify_yob", []byte(tt.input)) @@ -986,37 +981,31 @@ func TestVerifyYob(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } } func TestVerifyCreatePin(t *testing.T) { - fm, err := NewFlagManager(flagsPath) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - sessionId := "session123" // Create required mocks - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) flag_valid_pin, _ := fm.parser.GetFlag("flag_valid_pin") flag_pin_mismatch, _ := fm.parser.GetFlag("flag_pin_mismatch") flag_pin_set, _ := fm.parser.GetFlag("flag_pin_set") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - //Assuming this was the first set PIN to verify against - firstSetPin := "1234" h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, st: mockState, } @@ -1043,16 +1032,12 @@ func TestVerifyCreatePin(t *testing.T) { }, } - typ := utils.DATA_TEMPORARY_VALUE - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, typ).Return([]byte(firstSetPin), nil) - - // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(firstSetPin)).Return(nil) + err = store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte("1234")) + if err != nil { + t.Fatal(err) + } // Call the method under test res, err := h.VerifyCreatePin(ctx, "verify_create_pin", []byte(tt.input)) @@ -1062,36 +1047,32 @@ func TestVerifyCreatePin(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } } func TestCheckAccountStatus(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - sessionId := "session123" flag_account_success, _ := fm.GetFlag("flag_account_success") flag_account_pending, _ := fm.GetFlag("flag_account_pending") flag_api_error, _ := fm.GetFlag("flag_api_call_error") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - tests := []struct { name string - input []byte + publicKey []byte serverResponse *api.OKResponse - response *models.TrackStatusResponse expectedResult resource.Result }{ { - name: "Test when account is on the Sarafu network", - input: []byte("TrackingId1234"), + name: "Test when account is on the Sarafu network", + publicKey: []byte("TrackingId1234"), serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", @@ -1099,54 +1080,14 @@ func TestCheckAccountStatus(t *testing.T) { "active": true, }, }, - response: &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_success}, FlagReset: []uint32{flag_api_error, flag_account_pending}, }, }, { - name: "Test when the account is not yet on the sarafu network", - input: []byte("TrackingId1234"), - response: &models.TrackStatusResponse{ - Ok: true, - Result: struct { - Transaction struct { - CreatedAt time.Time "json:\"createdAt\"" - Status string "json:\"status\"" - TransferValue json.Number "json:\"transferValue\"" - TxHash string "json:\"txHash\"" - TxType string "json:\"txType\"" - } - }{ - Transaction: models.Transaction{ - CreatedAt: time.Now(), - Status: "SUCCESS", - TransferValue: json.Number("0.5"), - TxHash: "0x123abc456def", - TxType: "transfer", - }, - }, - }, + name: "Test when the account is not yet on the sarafu network", + publicKey: []byte("TrackingId1234"), serverResponse: &api.OKResponse{ Ok: true, Description: "Account creation succeeded", @@ -1162,59 +1103,51 @@ func TestCheckAccountStatus(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } - status := tt.response.Result.Transaction.Status - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return(tt.input, nil) + err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(tt.publicKey)) + if err != nil { + t.Fatal(err) + } - mockCreateAccountService.On("CheckAccountStatus", string(tt.input)).Return(tt.response, nil) - mockCreateAccountService.On("TrackAccountStatus", string(tt.input)).Return(tt.serverResponse, nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_STATUS, []byte(status)).Return(nil).Maybe() + mockAccountService.On("TrackAccountStatus", string(tt.publicKey)).Return(tt.serverResponse, nil) // Call the method under test - res, _ := h.CheckAccountStatus(ctx, "check_account_status", tt.input) + res, _ := h.CheckAccountStatus(ctx, "check_account_status", []byte("")) // Assert that no errors occurred assert.NoError(t, err) //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } - } func TestTransactionReset(t *testing.T) { - fm, err := NewFlagManager(flagsPath) + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } + flag_invalid_recipient, _ := fm.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := fm.GetFlag("flag_invalid_recipient_with_invite") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) - - sessionId := "session123" - - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1233,9 +1166,6 @@ func TestTransactionReset(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, []byte("")).Return(nil) - // Call the method under test res, _ := h.TransactionReset(ctx, "transaction_reset", tt.input) @@ -1244,40 +1174,32 @@ func TestTransactionReset(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } } -func TestResetInvalidAmount(t *testing.T) { +func TestResetTransactionAmount(t *testing.T) { sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { name string - input []byte - status string expectedResult resource.Result }{ { @@ -1289,43 +1211,34 @@ func TestResetInvalidAmount(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, []byte("")).Return(nil) - // Call the method under test - res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", tt.input) + res, _ := h.ResetTransactionAmount(ctx, "transaction_reset_amount", []byte("")) // Assert that no errors occurred assert.NoError(t, err) //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } - } func TestInitiateTransaction(t *testing.T) { sessionId := "254712345678" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } - account_authorized_flag, err := fm.parser.GetFlag("flag_account_authorized") + account_authorized_flag, _ := fm.parser.GetFlag("flag_account_authorized") - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } @@ -1351,10 +1264,18 @@ func TestInitiateTransaction(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Define expected interactions with the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_AMOUNT).Return(tt.Amount, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_RECIPIENT).Return(tt.Recipient, nil) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return(tt.ActiveSym, nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_AMOUNT, []byte(tt.Amount)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, utils.DATA_RECIPIENT, []byte(tt.Recipient)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.ActiveSym)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) @@ -1364,31 +1285,25 @@ func TestInitiateTransaction(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) }) } } func TestQuit(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } flag_account_authorized, _ := fm.parser.GetFlag("flag_account_authorized") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1417,13 +1332,10 @@ func TestQuit(t *testing.T) { //Assert that the account created flag has been set to the result assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - }) } } + func TestIsValidPIN(t *testing.T) { tests := []struct { name string @@ -1479,21 +1391,22 @@ func TestIsValidPIN(t *testing.T) { func TestValidateAmount(t *testing.T) { fm, err := NewFlagManager(flagsPath) - if err != nil { t.Logf(err.Error()) } - flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + flag_invalid_amount, _ := fm.parser.GetFlag("flag_invalid_amount") + + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, flagManager: fm.parser, } tests := []struct { @@ -1533,11 +1446,10 @@ func TestValidateAmount(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Mock behavior for active balance retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL).Return(tt.activeBal, nil) - - // Mock behavior for storing the amount (if valid) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_AMOUNT, tt.input).Return(nil).Maybe() + err := store.WriteEntry(ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) + if err != nil { + t.Fatal(err) + } // Call the method under test res, _ := h.ValidateAmount(ctx, "test_validate_amount", tt.input) @@ -1547,26 +1459,22 @@ func TestValidateAmount(t *testing.T) { // Assert the result matches the expected result assert.Equal(t, tt.expectedResult, res, "Expected result should match actual result") - - // Assert all expectations were met - mockDataStore.AssertExpectations(t) }) } } func TestValidateRecipient(t *testing.T) { fm, err := NewFlagManager(flagsPath) - - flag_invalid_recipient, _ := fm.parser.GetFlag("flag_invalid_recipient") - mockDataStore := new(mocks.MockUserDataStore) - - sessionId := "session123" - - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - if err != nil { log.Fatal(err) } + + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + flag_invalid_recipient, _ := fm.parser.GetFlag("flag_invalid_recipient") + // Define test cases tests := []struct { name string @@ -1590,13 +1498,10 @@ func TestValidateRecipient(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_RECIPIENT, tt.input).Return(nil) - - // Create the Handlers instance with the mock flag manager + // Create the Handlers instance h := &Handlers{ flagManager: fm.parser, - userdataStore: mockDataStore, + userdataStore: store, } // Call the method @@ -1605,14 +1510,16 @@ func TestValidateRecipient(t *testing.T) { if err != nil { t.Error(err) } + // Assert that the Result FlagSet has the required flags after language switch assert.Equal(t, res, tt.expectedResult, "Result should contain flag(s) that have been reset") - }) } } func TestCheckBalance(t *testing.T) { + ctx, store := InitializeTestStore(t) + tests := []struct { name string sessionId string @@ -1624,7 +1531,7 @@ func TestCheckBalance(t *testing.T) { }{ { name: "User with active sym", - sessionId: "session456", + sessionId: "session123", publicKey: "0X98765432109", activeSym: "ETH", activeBal: "1.5", @@ -1635,18 +1542,22 @@ func TestCheckBalance(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) mockAccountService := new(mocks.MockAccountService) - ctx := context.WithValue(context.Background(), "SessionId", tt.sessionId) + ctx := context.WithValue(ctx, "SessionId", tt.sessionId) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, accountService: mockAccountService, } - // Mock for user with active sym - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(tt.activeSym), nil) - mockDataStore.On("ReadEntry", ctx, tt.sessionId, utils.DATA_ACTIVE_BAL).Return([]byte(tt.activeBal), nil) + err := store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_SYM, []byte(tt.activeSym)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, tt.sessionId, utils.DATA_ACTIVE_BAL, []byte(tt.activeBal)) + if err != nil { + t.Fatal(err) + } res, err := h.CheckBalance(ctx, "check_balance", []byte("")) @@ -1657,23 +1568,21 @@ func TestCheckBalance(t *testing.T) { assert.Equal(t, tt.expectedResult, res, "Result should match expected output") } - mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) }) } } func TestGetProfile(t *testing.T) { - sessionId := "session123" + ctx, store := InitializeTestStore(t) - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) h := &Handlers{ - userdataStore: mockDataStore, - accountService: mockCreateAccountService, + userdataStore: store, + accountService: mockAccountService, st: mockState, } @@ -1697,7 +1606,7 @@ func TestGetProfile(t *testing.T) { }, }, { - name: "Test with with profile information in swa ", + name: "Test with with profile information in swa", keys: []utils.DataTyp{utils.DATA_FAMILY_NAME, utils.DATA_FIRST_NAME, utils.DATA_GENDER, utils.DATA_OFFERINGS, utils.DATA_LOCATION, utils.DATA_YOB}, profileInfo: []string{"Doee", "John", "Male", "Bananas", "Kilifi", "1976"}, languageCode: "swa", @@ -1723,22 +1632,21 @@ func TestGetProfile(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Language", lang.Language{ Code: tt.languageCode, }) for index, key := range tt.keys { - mockDataStore.On("ReadEntry", ctx, sessionId, key).Return([]byte(tt.profileInfo[index]), nil).Maybe() + err := store.WriteEntry(ctx, sessionId, key, []byte(tt.profileInfo[index])) + if err != nil { + t.Fatal(err) + } } res, _ := h.GetProfileInfo(ctx, "get_profile_info", []byte("")) - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - //Assert that the result set to content is what was expected assert.Equal(t, res, tt.result, "Result should contain profile information served back to user") - }) } } @@ -1749,12 +1657,10 @@ func TestVerifyNewPin(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_valid_pin, _ := fm.parser.GetFlag("flag_valid_pin") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, flagManager: fm.parser, - accountService: mockCreateAccountService, + accountService: mockAccountService, } ctx := context.WithValue(context.Background(), "SessionId", sessionId) @@ -1777,44 +1683,32 @@ func TestVerifyNewPin(t *testing.T) { FlagReset: []uint32{flag_valid_pin}, }, }, - { - name: "Test with invalid pin", - input: []byte("12345"), - expectedResult: resource.Result{ - FlagReset: []uint32{flag_valid_pin}, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - //Call the function under test res, _ := h.VerifyNewPin(ctx, "verify_new_pin", tt.input) - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - //Assert that the result set to content is what was expected assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") - }) } - } func TestConfirmPin(t *testing.T) { sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, _ := NewFlagManager(flagsPath) flag_pin_mismatch, _ := fm.parser.GetFlag("flag_pin_mismatch") - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, - accountService: mockCreateAccountService, + accountService: mockAccountService, } - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { name string @@ -1834,16 +1728,14 @@ func TestConfirmPin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Set up the expected behavior of the mock - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACCOUNT_PIN, []byte(tt.temporarypin)).Return(nil) - - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return(tt.temporarypin, nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(tt.temporarypin)) + if err != nil { + t.Fatal(err) + } //Call the function under test res, _ := h.ConfirmPinChange(ctx, "confirm_pin_change", tt.temporarypin) - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - //Assert that the result set to content is what was expected assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") @@ -1861,7 +1753,14 @@ func TestFetchCustodialBalances(t *testing.T) { // Define test data sessionId := "session123" publicKey := "0X13242618721" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + err = store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } tests := []struct { name string @@ -1903,50 +1802,39 @@ func TestFetchCustodialBalances(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - - mockDataStore := new(mocks.MockUserDataStore) - mockCreateAccountService := new(mocks.MockAccountService) + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) - // Create the Handlers instance with the mock store h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, st: mockState, - accountService: mockCreateAccountService, + accountService: mockAccountService, } // Set up the expected behavior of the mock - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) - mockCreateAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) + mockAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResonse, nil) // Call the method res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) - // Assert that expectations were met - mockDataStore.AssertExpectations(t) - //Assert that the result set to content is what was expected - assert.Equal(t, res, tt.expectedResult, "Result should contain flags set according to user input") - + assert.Equal(t, res, tt.expectedResult, "Result should match expected result") }) } } func TestSetDefaultVoucher(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + fm, err := NewFlagManager(flagsPath) if err != nil { t.Logf(err.Error()) } - flag_no_active_voucher, err := fm.GetFlag("flag_no_active_voucher") - if err != nil { - t.Logf(err.Error()) - } - // Define session ID and mock data - sessionId := "session123" + publicKey := "0X13242618721" - notFoundErr := db.ErrNotFound{} - ctx := context.WithValue(context.Background(), "SessionId", sessionId) tests := []struct { name string @@ -1954,7 +1842,7 @@ func TestSetDefaultVoucher(t *testing.T) { expectedResult resource.Result }{ { - name: "Test set default voucher when no active voucher exists", + name: "Test set default voucher when no active voucher is set", vouchersResp: &models.VoucherHoldingResponse{ Ok: true, Description: "Vouchers fetched successfully", @@ -1971,77 +1859,55 @@ func TestSetDefaultVoucher(t *testing.T) { }, expectedResult: resource.Result{}, }, - { - name: "Test no vouchers available", - vouchersResp: &models.VoucherHoldingResponse{ - Ok: true, - Description: "No vouchers available", - Result: models.VoucherResult{ - Holdings: []dataserviceapi.TokenHoldings{}, - }, - }, - expectedResult: resource.Result{ - FlagSet: []uint32{flag_no_active_voucher}, - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) mockAccountService := new(mocks.MockAccountService) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, accountService: mockAccountService, flagManager: fm.parser, } - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM).Return([]byte(""), notFoundErr) - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } mockAccountService.On("FetchVouchers", string(publicKey)).Return(tt.vouchersResp, nil) - if len(tt.vouchersResp.Result.Holdings) > 0 { - firstVoucher := tt.vouchersResp.Result.Holdings[0] - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_SYM, []byte(firstVoucher.TokenSymbol)).Return(nil) - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_ACTIVE_BAL, []byte(firstVoucher.Balance)).Return(nil) - } - - res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("some-input")) + res, err := h.SetDefaultVoucher(ctx, "set_default_voucher", []byte("")) assert.NoError(t, err) assert.Equal(t, res, tt.expectedResult, "Expected result should be equal to the actual result") - mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) }) } } func TestCheckVouchers(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) mockAccountService := new(mocks.MockAccountService) - sessionId := "session123" publicKey := "0X13242618721" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, accountService: mockAccountService, prefixDb: spdb, } - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_PUBLIC_KEY).Return([]byte(publicKey), nil) + err := store.WriteEntry(ctx, sessionId, utils.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } mockVouchersResponse := &models.VoucherHoldingResponse{} mockVouchersResponse.Result.Holdings = []dataserviceapi.TokenHoldings{ @@ -2065,7 +1931,6 @@ func TestCheckVouchers(t *testing.T) { // assert that the data is stored correctly assert.Equal(t, expectedSym, voucherData) - mockDataStore.AssertExpectations(t) mockAccountService.AssertExpectations(t) } @@ -2073,12 +1938,7 @@ func TestGetVoucherList(t *testing.T) { sessionId := "session123" ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err := db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ prefixDb: spdb, @@ -2087,7 +1947,7 @@ func TestGetVoucherList(t *testing.T) { expectedSym := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err = spdb.Put(ctx, []byte("sym"), expectedSym) + err := spdb.Put(ctx, []byte("sym"), expectedSym) if err != nil { t.Fatal(err) } @@ -2103,20 +1963,15 @@ func TestViewVoucher(t *testing.T) { if err != nil { t.Logf(err.Error()) } - mockDataStore := new(mocks.MockUserDataStore) - + ctx, store := InitializeTestStore(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) - db := memdb.NewMemDb() - err = db.Connect(ctx, "") - if err != nil { - t.Fatal(err) - } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + spdb := InitializeTestSubPrefixDb(t, ctx) h := &Handlers{ - userdataStore: mockDataStore, + userdataStore: store, flagManager: fm.parser, prefixDb: spdb, } @@ -2137,23 +1992,20 @@ func TestViewVoucher(t *testing.T) { } } - // Set up expectations for mockDataStore - expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "100", "6", "0xd4c288865Ce") - - mockDataStore.On("WriteEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)).Return(nil) - res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) - assert.Contains(t, res.Content, "SRF\n100") - - mockDataStore.AssertExpectations(t) + assert.Equal(t, res.Content, "SRF\n100") } func TestSetVoucher(t *testing.T) { - mockDataStore := new(mocks.MockUserDataStore) - + ctx, store := InitializeTestStore(t) sessionId := "session123" - ctx := context.WithValue(context.Background(), "SessionId", sessionId) + + ctx = context.WithValue(ctx, "SessionId", sessionId) + + h := &Handlers{ + userdataStore: store, + } // Define the temporary voucher data tempData := &dataserviceapi.TokenHoldings{ @@ -2163,33 +2015,16 @@ func TestSetVoucher(t *testing.T) { ContractAddress: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", } - expectedData := fmt.Sprintf("%s,%s,%s,%s", "SRF", "200", "6", "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9") + expectedData := fmt.Sprintf("%s,%s,%s,%s", tempData.TokenSymbol, tempData.Balance, tempData.TokenDecimals, tempData.ContractAddress) - // Define the expected active entries - activeEntries := map[utils.DataTyp][]byte{ - utils.DATA_ACTIVE_SYM: []byte(tempData.TokenSymbol), - utils.DATA_ACTIVE_BAL: []byte(tempData.Balance), - utils.DATA_ACTIVE_DECIMAL: []byte(tempData.TokenDecimals), - utils.DATA_ACTIVE_ADDRESS: []byte(tempData.ContractAddress), + // store the expectedData + if err := store.WriteEntry(ctx, sessionId, utils.DATA_TEMPORARY_VALUE, []byte(expectedData)); err != nil { + t.Fatal(err) } - // Mocking ReadEntry calls for temporary data retrieval - mockDataStore.On("ReadEntry", ctx, sessionId, utils.DATA_TEMPORARY_VALUE).Return([]byte(expectedData), nil) - - // Mocking WriteEntry calls for setting active data - for key, value := range activeEntries { - mockDataStore.On("WriteEntry", ctx, sessionId, key, value).Return(nil) - } - - h := &Handlers{ - userdataStore: mockDataStore, - } - - res, err := h.SetVoucher(ctx, "someSym", []byte{}) + res, err := h.SetVoucher(ctx, "set_voucher", []byte{}) assert.NoError(t, err) assert.Equal(t, string(tempData.TokenSymbol), res.Content) - - mockDataStore.AssertExpectations(t) } From e29a24b376cff982367fe2c00470ee1af8eea5cd Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 15:46:46 +0000 Subject: [PATCH 080/332] Add missing models files --- common/hex.go | 13 +++++++++++++ internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 20 ++++++++++---------- internal/models/tokenresponse.go | 18 ++++++++++++++++++ internal/models/vouchersresponse.go | 14 ++++++++++++++ 5 files changed, 56 insertions(+), 10 deletions(-) create mode 100644 internal/models/tokenresponse.go create mode 100644 internal/models/vouchersresponse.go diff --git a/common/hex.go b/common/hex.go index f5aa7ed..971ecf1 100644 --- a/common/hex.go +++ b/common/hex.go @@ -2,6 +2,7 @@ package common import ( "encoding/hex" + "strings" ) func NormalizeHex(s string) (string, error) { @@ -16,3 +17,15 @@ func NormalizeHex(s string) (string, error) { } return hex.EncodeToString(r), nil } + +func IsSameHex(left string, right string) bool { + bl, err := NormalizeHex(left) + if err != nil { + return false + } + br, err := NormalizeHex(left) + if err != nil { + return false + } + return strings.Compare(bl, br) == 0 +} diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 9afba13..7d8325c 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -10,6 +10,7 @@ import ( "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/handlers/ussd" + "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/remote" ) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dc77660..a7e9d88 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -233,11 +233,11 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt return res, fmt.Errorf("missing session") } store := h.userdataStore - blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } @@ -307,12 +307,12 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } temporaryPin := string(input) - blockedNumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedNumber), utils.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { return res, err } @@ -841,15 +841,15 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) if !ok { return res, fmt.Errorf("missing session") } - blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { return res, err } - temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), utils.DATA_TEMPORARY_VALUE) + temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) if err != nil { return res, err } - err = store.WriteEntry(ctx, string(blockedPhonenumber), utils.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, nil } @@ -874,7 +874,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } blockedNumber := string(input) - _, err = store.ReadEntry(ctx, blockedNumber, utils.DATA_PUBLIC_KEY) + _, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY) if !isValidPhoneNumber(blockedNumber) { res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil @@ -888,7 +888,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, err } } - err = store.WriteEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) + err = store.WriteEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER, []byte(blockedNumber)) if err != nil { return res, nil } @@ -1075,7 +1075,7 @@ func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } store := h.userdataStore - blockedNumber, _ := store.ReadEntry(ctx, sessionId, utils.DATA_BLOCKED_NUMBER) + blockedNumber, _ := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) res.Content = string(blockedNumber) diff --git a/internal/models/tokenresponse.go b/internal/models/tokenresponse.go new file mode 100644 index 0000000..d243d93 --- /dev/null +++ b/internal/models/tokenresponse.go @@ -0,0 +1,18 @@ +package models + +type ApiResponse struct { + OK bool `json:"ok"` + Description string `json:"description"` + Result Result `json:"result"` +} + +type Result struct { + Holdings []Holding `json:"holdings"` +} + +type Holding struct { + ContractAddress string `json:"contractAddress"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + Balance string `json:"balance"` +} diff --git a/internal/models/vouchersresponse.go b/internal/models/vouchersresponse.go new file mode 100644 index 0000000..09b085d --- /dev/null +++ b/internal/models/vouchersresponse.go @@ -0,0 +1,14 @@ +package models + +import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" + +type VoucherHoldingResponse struct { + Ok bool `json:"ok"` + Description string `json:"description"` + Result VoucherResult `json:"result"` +} + +// VoucherResult holds the list of token holdings +type VoucherResult struct { + Holdings []dataserviceapi.TokenHoldings `json:"holdings"` +} From 14bc11f4bd87574526b453339e4b079967216b83 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 16:38:29 +0000 Subject: [PATCH 081/332] Add transaction getter --- common/db.go | 2 +- common/vouchers.go | 9 +++++++++ remote/accountservice.go | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/common/db.go b/common/db.go index 58fd3a1..06d9607 100644 --- a/common/db.go +++ b/common/db.go @@ -30,7 +30,7 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS - + DATA_TRANSACTIONS ) func typToBytes(typ DataTyp) []byte { diff --git a/common/vouchers.go b/common/vouchers.go index 9ca6277..cd76060 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -37,6 +37,15 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { return data } +//func StoreVouchers(db storage.PrefixDb, data VoucherMetadata) { +// value, err := db.Put(ctx, []byte(key)) +// if err != nil { +// return nil, fmt.Errorf("failed to get %s: %v", key, err) +// } +// data[key] = string(value) +// } +//} + // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []string{"sym", "bal", "deci", "addr"} diff --git a/remote/accountservice.go b/remote/accountservice.go index bc6a448..d0d7e9b 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -22,6 +22,7 @@ type AccountServiceInterface interface { CreateAccount(ctx context.Context) (*models.AccountResult, error) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) + FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) } type AccountService struct { @@ -100,13 +101,18 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes return &r, nil } -// FetchVouchers retrieves the token holdings for a given public key from the custodial holdings API endpoint +// FetchVouchers retrieves the token holdings for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { var r []dataserviceapi.TokenHoldings - req, err := http.NewRequest("GET", config.VoucherHoldingsURL, nil) + ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) if err != nil { return nil, err } @@ -119,6 +125,32 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return r, nil } + +// FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint +// Parameters: +// - publicKey: The public key associated with the account. +func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + var r []dataserviceapi.Last10TxResponse + + ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doDataRequest(ctx, req, r) + if err != nil { + return nil, err + } + + return r, nil +} + + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse From 3a8a5f40baa56612e8435387542bbd2fcf16368b Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 16:42:50 +0000 Subject: [PATCH 082/332] Add test service placeholders for fetchtransactions --- internal/testutil/mocks/servicemock.go | 5 +++++ internal/testutil/testservice/TestAccountService.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index bec48fd..78ee2b0 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -33,3 +33,8 @@ func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string args := m.Called(publicKey) return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) } + +func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + args := m.Called(publicKey) + return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index a9a3085..3c104bd 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -42,3 +42,7 @@ func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey stri }, }, nil } + +func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { + return []dataserviceapi.Last10TxResponse{}, nil +} From 10b3083647e90968dff2d1c5d73280515235ac20 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 20:29:32 +0000 Subject: [PATCH 083/332] WIP Add db dumper tool --- devtools/store/main.go | 64 ++++++++++++++++++++++++++++++++++++++++ go.mod | 4 +-- go.sum | 4 +-- internal/storage/gdbm.go | 4 +++ 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 devtools/store/main.go diff --git a/devtools/store/main.go b/devtools/store/main.go new file mode 100644 index 0000000..df27bb1 --- /dev/null +++ b/devtools/store/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "path" + + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" + "git.grassecon.net/urdt/ussd/internal/storage" + "git.defalsify.org/vise.git/logging" +) + +var ( + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") +) + +func init() { + initializers.LoadEnvVariables() +} + +func main() { + config.LoadConfig() + + var dbDir string + var sessionId string + var database string + var engineDebug bool + flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") + flag.StringVar(&database, "db", "gdbm", "database to be used") + flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") + flag.BoolVar(&engineDebug, "d", false, "use engine debug output") + flag.Parse() + + ctx := context.Background() + //ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Database", database) + + resourceDir := scriptDir + menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) + + store, err := menuStorageService.GetUserdataDb(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + d, err := store.Dump(ctx, []byte(sessionId)) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + for true { + k, v := d.Next(ctx) + if k == nil { + break + } + fmt.Printf("%x %s\n", k, v) + } +} diff --git a/go.mod b/go.mod index 391c1a5..e30d42f 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,14 @@ go 1.23.0 toolchain go1.23.2 require ( - git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b + git.defalsify.org/vise.git v0.2.1-0.20241102200556-5348e1988045 github.com/alecthomas/assert/v2 v2.2.2 github.com/grassrootseconomics/eth-custodial v1.3.0-beta github.com/peteole/testdata-loader v0.3.0 gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) -require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect +require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a require ( github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0ba38c1..21dbfb2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241102200556-5348e1988045 h1:uFFVEymnsJGVcuI2r//oNAqDD8l0HOkkEvqpOJHdJiY= +git.defalsify.org/vise.git v0.2.1-0.20241102200556-5348e1988045/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go index 49de570..adb1d0f 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm.go @@ -114,3 +114,7 @@ func(tdb *ThreadGdbmDb) Close() error { tdb.db = nil return err } + +func(tdb *ThreadGdbmDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) { + return tdb.db.Dump(ctx, key) +} From 1d77ad98dc6e4d7be4a3f63561815c06d07039b2 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 23:33:52 +0000 Subject: [PATCH 084/332] Expose subprefix db --- common/storage.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 common/storage.go diff --git a/common/storage.go b/common/storage.go new file mode 100644 index 0000000..62a8a06 --- /dev/null +++ b/common/storage.go @@ -0,0 +1,13 @@ +package common + +import ( + "git.defalsify.org/vise.git/db" +) + +func StoreToDb(store *UserDataStore, prefix []byte) db.Db { + innerStore := store.Db + if pfx != nil { + innerStore = NewSubPrefixDb(innerStore, pfx) + } + return innerStore +} From dae12ac4989e40f64a48930b5348288c85af8369 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 2 Nov 2024 23:41:08 +0000 Subject: [PATCH 085/332] Separate subprefix db export --- common/storage.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/common/storage.go b/common/storage.go index 62a8a06..145382d 100644 --- a/common/storage.go +++ b/common/storage.go @@ -2,12 +2,14 @@ package common import ( "git.defalsify.org/vise.git/db" + + "git.grassecon.net/urdt/ussd/internal/storage" ) -func StoreToDb(store *UserDataStore, prefix []byte) db.Db { - innerStore := store.Db - if pfx != nil { - innerStore = NewSubPrefixDb(innerStore, pfx) - } - return innerStore +func StoreToDb(store *UserDataStore) db.Db { + return store.Db +} + +func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { + return storage.NewSubPrefixDb(store.Db, pfx) } From a237b615f2ee3148146883e9267ac3c4f77f35e7 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 01:44:57 +0000 Subject: [PATCH 086/332] Export models package --- common/user_store.go | 1 + internal/handlers/ussd/menuhandler_test.go | 2 +- internal/testutil/mocks/servicemock.go | 2 +- internal/testutil/testservice/TestAccountService.go | 2 +- {internal/models => models}/accountresponse.go | 0 {internal/models => models}/balanceresponse.go | 0 {internal/models => models}/tokenresponse.go | 0 {internal/models => models}/trackstatusresponse.go | 0 {internal/models => models}/vouchersresponse.go | 0 remote/accountservice.go | 2 +- 10 files changed, 5 insertions(+), 4 deletions(-) rename {internal/models => models}/accountresponse.go (100%) rename {internal/models => models}/balanceresponse.go (100%) rename {internal/models => models}/tokenresponse.go (100%) rename {internal/models => models}/trackstatusresponse.go (100%) rename {internal/models => models}/vouchersresponse.go (100%) diff --git a/common/user_store.go b/common/user_store.go index 4b8409f..29796e2 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -25,6 +25,7 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ } // WriteEntry adds an entry to the userdata store. +// BUG: this uses sessionId twice func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error { store.SetPrefix(db.DATATYPE_USERDATA) store.SetSession(sessionId) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 86c905d..74e9f53 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -14,7 +14,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 78ee2b0..a24e459 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -3,7 +3,7 @@ package mocks import ( "context" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/stretchr/testify/mock" ) diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 3c104bd..21f7a63 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) diff --git a/internal/models/accountresponse.go b/models/accountresponse.go similarity index 100% rename from internal/models/accountresponse.go rename to models/accountresponse.go diff --git a/internal/models/balanceresponse.go b/models/balanceresponse.go similarity index 100% rename from internal/models/balanceresponse.go rename to models/balanceresponse.go diff --git a/internal/models/tokenresponse.go b/models/tokenresponse.go similarity index 100% rename from internal/models/tokenresponse.go rename to models/tokenresponse.go diff --git a/internal/models/trackstatusresponse.go b/models/trackstatusresponse.go similarity index 100% rename from internal/models/trackstatusresponse.go rename to models/trackstatusresponse.go diff --git a/internal/models/vouchersresponse.go b/models/vouchersresponse.go similarity index 100% rename from internal/models/vouchersresponse.go rename to models/vouchersresponse.go diff --git a/remote/accountservice.go b/remote/accountservice.go index d0d7e9b..402add5 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -11,7 +11,7 @@ import ( dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" - "git.grassecon.net/urdt/ussd/internal/models" + "git.grassecon.net/urdt/ussd/models" ) var ( From 0506a8c452f1c5daab3383d4c3248a0b0780dbdf Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 14:34:26 +0000 Subject: [PATCH 087/332] Add voucherdata endpoint --- common/db.go | 6 ++++++ common/vouchers.go | 2 ++ config/config.go | 3 +++ models/vouchersresponse.go | 17 ++++++++++++----- remote/accountservice.go | 21 +++++++++++++++++++++ 5 files changed, 44 insertions(+), 5 deletions(-) diff --git a/common/db.go b/common/db.go index 06d9607..91891d9 100644 --- a/common/db.go +++ b/common/db.go @@ -2,6 +2,8 @@ package common import ( "encoding/binary" + + "git.defalsify.org/vise.git/logging" ) type DataTyp uint16 @@ -33,6 +35,10 @@ const ( DATA_TRANSACTIONS ) +var ( + logg = logging.NewVanilla().WithDomain("urdt-common") +) + func typToBytes(typ DataTyp) []byte { var b [2]byte binary.BigEndian.PutUint16(b[:], uint16(typ)) diff --git a/common/vouchers.go b/common/vouchers.go index cd76060..2fed043 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -84,6 +84,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, decList := strings.Split(decimals, "\n") addrList := strings.Split(addresses, "\n") + logg.Tracef("found" , "symlist", symList, "syms", symbols, "input", input) for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) @@ -136,6 +137,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str // UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { + logg.TraceCtxf(ctx, "dtal", "data", data) // Active voucher data entries activeEntries := map[DataTyp][]byte{ DATA_ACTIVE_SYM: []byte(data.TokenSymbol), diff --git a/config/config.go b/config/config.go index a5c89a7..fbf518b 100644 --- a/config/config.go +++ b/config/config.go @@ -13,6 +13,7 @@ const ( trackPath = "/api/v2/account/status" voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" + voucherDataPathPrefix = "/api/v1/token" ) var ( @@ -29,6 +30,7 @@ var ( TrackURL string VoucherHoldingsURL string VoucherTransfersURL string + VoucherDataURL string ) func setBase() error { @@ -62,6 +64,7 @@ func LoadConfig() error { TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) + VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) return nil } diff --git a/models/vouchersresponse.go b/models/vouchersresponse.go index 09b085d..8cf3ec6 100644 --- a/models/vouchersresponse.go +++ b/models/vouchersresponse.go @@ -2,13 +2,20 @@ package models import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" -type VoucherHoldingResponse struct { - Ok bool `json:"ok"` - Description string `json:"description"` - Result VoucherResult `json:"result"` -} +//type VoucherHoldingResponse struct { +// Ok bool `json:"ok"` +// Description string `json:"description"` +// Result VoucherResult `json:"result"` +//} // VoucherResult holds the list of token holdings type VoucherResult struct { Holdings []dataserviceapi.TokenHoldings `json:"holdings"` } + +type VoucherDataResult struct { + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + SinkAddress string `json:"sinkAddress"` +} diff --git a/remote/accountservice.go b/remote/accountservice.go index 402add5..73052f6 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -23,6 +23,7 @@ type AccountServiceInterface interface { TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) + VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) } type AccountService struct { @@ -151,6 +152,26 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin } +// VoucherData retrieves voucher metadata from the data indexer API endpoint. +// Parameters: +// - address: The voucher address. +func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + var voucherDataResult models.VoucherDataResult + + ep, err := url.JoinPath(config.VoucherDataURL, address) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doCustodialRequest(ctx, req, &voucherDataResult) + return &voucherDataResult, err +} + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse From 7189235bee57b4fc703c5d7a2f43f0b4a660c397 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 3 Nov 2024 15:14:17 +0000 Subject: [PATCH 088/332] Remove unused data type --- common/db.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/db.go b/common/db.go index 91891d9..1992476 100644 --- a/common/db.go +++ b/common/db.go @@ -25,7 +25,6 @@ const ( DATA_RECIPIENT DATA_AMOUNT DATA_TEMPORARY_VALUE - DATA_VOUCHER_LIST DATA_ACTIVE_SYM DATA_ACTIVE_BAL DATA_BLOCKED_NUMBER From 2c30ccc4058f821f7996442963defced49e4146e Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 4 Nov 2024 02:23:30 +0000 Subject: [PATCH 089/332] Add voucherdata call to test accountservices --- common/storage.go | 39 ++++++++++++++++++- internal/testutil/mocks/servicemock.go | 5 +++ .../testservice/TestAccountService.go | 4 ++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/common/storage.go b/common/storage.go index 145382d..dff4774 100644 --- a/common/storage.go +++ b/common/storage.go @@ -1,8 +1,12 @@ package common import ( - "git.defalsify.org/vise.git/db" + "context" + "errors" + "git.defalsify.org/vise.git/db" + "git.defalsify.org/vise.git/resource" + "git.defalsify.org/vise.git/persist" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -13,3 +17,36 @@ func StoreToDb(store *UserDataStore) db.Db { func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { return storage.NewSubPrefixDb(store.Db, pfx) } + +type StorageServices interface { + GetPersister(ctx context.Context) (*persist.Persister, error) + GetUserdataDb(ctx context.Context) (db.Db, error) + GetResource(ctx context.Context) (resource.Resource, error) + EnsureDbDir() error +} + +type StorageService struct { + svc *storage.MenuStorageService +} + +func NewStorageService(dbDir string) *StorageService { + return &StorageService{ + svc: storage.NewMenuStorageService(dbDir, ""), + } +} + +func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) { + return ss.svc.GetPersister(ctx) +} + +func(ss *StorageService) GetUserdataDb(ctx context.Context) (db.Db, error) { + return ss.svc.GetUserdataDb(ctx) +} + +func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) { + return nil, errors.New("not implemented") +} + +func(ss *StorageService) EnsureDbDir() error { + return ss.svc.EnsureDbDir() +} diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index a24e459..76803ba 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -38,3 +38,8 @@ func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey st args := m.Called(publicKey) return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) } + +func(m MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + args := m.Called(address) + return args.Get(0).(*models.VoucherDataResult), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 21f7a63..8752d6f 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -46,3 +46,7 @@ func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey stri func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { return []dataserviceapi.Last10TxResponse{}, nil } + +func(m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { + return &models.VoucherDataResult{}, nil +} From a9641fd70d85a653d0780c359e3cfaf617439501 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 4 Nov 2024 15:24:01 +0300 Subject: [PATCH 090/332] ensure swahili menu template --- services/registration/confirm_others_new_pin_swa | 1 + services/registration/enter_other_number_swa | 1 + services/registration/enter_others_new_pin_swa | 1 + services/registration/no_admin_privilege_swa | 1 + services/registration/others_pin_mismatch_swa | 1 + services/registration/pin_reset_mismatch_swa | 1 + services/registration/pin_reset_result_swa | 1 + services/registration/select_voucher_menu_swa | 1 + services/registration/voucher_details_menu_swa | 1 + 9 files changed, 9 insertions(+) create mode 100644 services/registration/confirm_others_new_pin_swa create mode 100644 services/registration/enter_other_number_swa create mode 100644 services/registration/enter_others_new_pin_swa create mode 100644 services/registration/no_admin_privilege_swa create mode 100644 services/registration/others_pin_mismatch_swa create mode 100644 services/registration/pin_reset_mismatch_swa create mode 100644 services/registration/pin_reset_result_swa create mode 100644 services/registration/select_voucher_menu_swa create mode 100644 services/registration/voucher_details_menu_swa diff --git a/services/registration/confirm_others_new_pin_swa b/services/registration/confirm_others_new_pin_swa new file mode 100644 index 0000000..f0b09c8 --- /dev/null +++ b/services/registration/confirm_others_new_pin_swa @@ -0,0 +1 @@ +Tafadhali thibitisha PIN mpya ya: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/enter_other_number_swa b/services/registration/enter_other_number_swa new file mode 100644 index 0000000..214fc4a --- /dev/null +++ b/services/registration/enter_other_number_swa @@ -0,0 +1 @@ +Weka nambari ya simu ili kutuma ombi la kubadilisha nambari ya siri: \ No newline at end of file diff --git a/services/registration/enter_others_new_pin_swa b/services/registration/enter_others_new_pin_swa new file mode 100644 index 0000000..77ec2f3 --- /dev/null +++ b/services/registration/enter_others_new_pin_swa @@ -0,0 +1 @@ +Tafadhali weka PIN mpya ya: {{.retrieve_blocked_number}} \ No newline at end of file diff --git a/services/registration/no_admin_privilege_swa b/services/registration/no_admin_privilege_swa new file mode 100644 index 0000000..6c6d3dc --- /dev/null +++ b/services/registration/no_admin_privilege_swa @@ -0,0 +1 @@ +Huna mapendeleo ya kufanya kitendo hiki \ No newline at end of file diff --git a/services/registration/others_pin_mismatch_swa b/services/registration/others_pin_mismatch_swa new file mode 100644 index 0000000..5787790 --- /dev/null +++ b/services/registration/others_pin_mismatch_swa @@ -0,0 +1 @@ +PIN uliyoweka hailingani.Jaribu tena. \ No newline at end of file diff --git a/services/registration/pin_reset_mismatch_swa b/services/registration/pin_reset_mismatch_swa new file mode 100644 index 0000000..5787790 --- /dev/null +++ b/services/registration/pin_reset_mismatch_swa @@ -0,0 +1 @@ +PIN uliyoweka hailingani.Jaribu tena. \ No newline at end of file diff --git a/services/registration/pin_reset_result_swa b/services/registration/pin_reset_result_swa new file mode 100644 index 0000000..30de04e --- /dev/null +++ b/services/registration/pin_reset_result_swa @@ -0,0 +1 @@ +Ombi la kuweka upya PIN ya {{.retrieve_blocked_number}} limefanikiwa \ No newline at end of file diff --git a/services/registration/select_voucher_menu_swa b/services/registration/select_voucher_menu_swa new file mode 100644 index 0000000..2cb4daf --- /dev/null +++ b/services/registration/select_voucher_menu_swa @@ -0,0 +1 @@ +Chagua Sarafu \ No newline at end of file diff --git a/services/registration/voucher_details_menu_swa b/services/registration/voucher_details_menu_swa new file mode 100644 index 0000000..e84661b --- /dev/null +++ b/services/registration/voucher_details_menu_swa @@ -0,0 +1 @@ +Maelezo ya Sarafu \ No newline at end of file From c77558689a5d2ee7a11e314dd2bca0c365b3f1b3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 4 Nov 2024 15:24:22 +0300 Subject: [PATCH 091/332] ensure swahlili menu template --- services/registration/address_swa | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/registration/address_swa diff --git a/services/registration/address_swa b/services/registration/address_swa new file mode 100644 index 0000000..3e7a55e --- /dev/null +++ b/services/registration/address_swa @@ -0,0 +1 @@ +Anwani:{{.check_identifier}} \ No newline at end of file From 2b865a365bd4dc5f33442619414e526ba0d00fe5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 4 Nov 2024 15:24:51 +0300 Subject: [PATCH 092/332] add back option to view address --- services/registration/address.vis | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/address.vis b/services/registration/address.vis index f3ba04a..dfc46d1 100644 --- a/services/registration/address.vis +++ b/services/registration/address.vis @@ -1,6 +1,8 @@ LOAD check_identifier 0 RELOAD check_identifier MAP check_identifier +MOUT back 0 MOUT quit 9 HALT +INCMP _ 0 INCMP quit 9 From 88b50c5dd77852b2cd88097973162e1090cc2e89 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 4 Nov 2024 15:52:03 +0300 Subject: [PATCH 093/332] Remove admin number defination in env example --- .env.example | 4 --- internal/handlers/ussd/menuhandler.go | 3 +-- internal/handlers/ussd/menuhandler_test.go | 31 +++++++++++----------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/.env.example b/.env.example index 4d86f8b..ab370a7 100644 --- a/.env.example +++ b/.env.example @@ -16,7 +16,3 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ TRACK_URL=http://localhost:5003/api/v2/account/status - - -#numbers with privileges to reset others pin -ADMIN_NUMBERS=254051722XXX,255012221XXX \ No newline at end of file diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a7e9d88..6c1917d 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -20,8 +20,8 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/common" - "git.grassecon.net/urdt/ussd/remote" "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" @@ -1390,7 +1390,6 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re return res, fmt.Errorf("missing session") } - // Get temporary data tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 2f2e94b..f4c9f7c 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -12,10 +12,10 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" + "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/common" "github.com/alecthomas/assert/v2" @@ -122,7 +122,7 @@ func TestCreateAccount(t *testing.T) { name: "Test account creation success", serverResponse: &models.AccountResult{ TrackingId: "1234567890", - PublicKey: "0xD3adB33f", + PublicKey: "0xD3adB33f", }, expectedResult: resource.Result{ FlagSet: []uint32{flag_account_created}, @@ -176,7 +176,7 @@ func TestSaveFirstname(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -215,7 +215,7 @@ func TestSaveFamilyname(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -254,7 +254,7 @@ func TestSaveYoB(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -265,7 +265,7 @@ func TestSaveYoB(t *testing.T) { // Define test data yob := "1980" - + if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)); err != nil { t.Fatal(err) } @@ -293,7 +293,7 @@ func TestSaveLocation(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -304,7 +304,7 @@ func TestSaveLocation(t *testing.T) { // Define test data location := "Kilifi" - + if err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)); err != nil { t.Fatal(err) } @@ -332,7 +332,7 @@ func TestSaveOfferings(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -371,7 +371,7 @@ func TestSaveGender(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - + fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") @@ -1746,9 +1746,9 @@ func TestFetchCustodialBalances(t *testing.T) { } tests := []struct { - name string + name string balanceResponse *models.BalanceResult - expectedResult resource.Result + expectedResult resource.Result }{ { name: "Test when fetch custodial balances is not a success", @@ -1807,16 +1807,15 @@ func TestSetDefaultVoucher(t *testing.T) { expectedResult resource.Result }{ { - name: "Test no vouchers available", - vouchersResp: []dataserviceapi.TokenHoldings { - }, + name: "Test no vouchers available", + vouchersResp: []dataserviceapi.TokenHoldings{}, expectedResult: resource.Result{ FlagSet: []uint32{flag_no_active_voucher}, }, }, { name: "Test set default voucher when no active voucher is set", - vouchersResp: []dataserviceapi.TokenHoldings { + vouchersResp: []dataserviceapi.TokenHoldings{ dataserviceapi.TokenHoldings{ ContractAddress: "0x123", TokenSymbol: "TOKEN1", From da91eed9d4559e3866a931574b827798dd832fd3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 4 Nov 2024 15:55:58 +0300 Subject: [PATCH 094/332] add retry menu option --- services/registration/retry_menu | 1 + services/registration/retry_menu_swa | 1 + 2 files changed, 2 insertions(+) create mode 100644 services/registration/retry_menu create mode 100644 services/registration/retry_menu_swa diff --git a/services/registration/retry_menu b/services/registration/retry_menu new file mode 100644 index 0000000..ffde86c --- /dev/null +++ b/services/registration/retry_menu @@ -0,0 +1 @@ +Retry \ No newline at end of file diff --git a/services/registration/retry_menu_swa b/services/registration/retry_menu_swa new file mode 100644 index 0000000..c43b419 --- /dev/null +++ b/services/registration/retry_menu_swa @@ -0,0 +1 @@ +Jaribu tena \ No newline at end of file From c46c31ea36fd14b61d0defe23e640185024dfda8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 4 Nov 2024 16:06:33 +0300 Subject: [PATCH 095/332] ensure swahili translation --- services/registration/unregistered_number_swa | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/registration/unregistered_number_swa diff --git a/services/registration/unregistered_number_swa b/services/registration/unregistered_number_swa new file mode 100644 index 0000000..19810cb --- /dev/null +++ b/services/registration/unregistered_number_swa @@ -0,0 +1 @@ +Nambari uliyoingiza haijasajiliwa na Sarafu au sio sahihi. \ No newline at end of file From 92ea3df4aa149118e1ecebe729cc48d944b2d2dc Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 4 Nov 2024 17:38:55 +0300 Subject: [PATCH 096/332] Removeearly return statement --- remote/accountservice.go | 1 - 1 file changed, 1 deletion(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 73052f6..2e19de1 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -202,7 +202,6 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons if len(okResponse.Result) == 0 { return nil, errors.New("Empty api result") } - return &okResponse, nil v, err := json.Marshal(okResponse.Result) if err != nil { From 266d3d06c3c86ec631b10011ca4e193ea1f367fe Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 4 Nov 2024 17:56:25 +0300 Subject: [PATCH 097/332] Check the flag_no_active_voucher before proceeding --- internal/handlers/ussd/menuhandler.go | 3 ++- services/registration/no_voucher | 2 +- services/registration/no_voucher_swa | 2 +- services/registration/select_voucher.vis | 1 + 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 6c1917d..ca4af09 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1254,7 +1254,8 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by // Fetch vouchers from the API using the public key vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey)) if err != nil { - return res, err + res.FlagSet = append(res.FlagSet, flag_no_active_voucher) + return res, nil } // Return if there is no voucher diff --git a/services/registration/no_voucher b/services/registration/no_voucher index 332f00e..6303197 100644 --- a/services/registration/no_voucher +++ b/services/registration/no_voucher @@ -1 +1 @@ -You need a voucher to send \ No newline at end of file +You need a voucher to proceed \ No newline at end of file diff --git a/services/registration/no_voucher_swa b/services/registration/no_voucher_swa index 66e8f26..7291650 100644 --- a/services/registration/no_voucher_swa +++ b/services/registration/no_voucher_swa @@ -1 +1 @@ -Unahitaji sarafu kutuma \ No newline at end of file +Unahitaji sarafu kuendelea \ No newline at end of file diff --git a/services/registration/select_voucher.vis b/services/registration/select_voucher.vis index 08aa434..058d791 100644 --- a/services/registration/select_voucher.vis +++ b/services/registration/select_voucher.vis @@ -1,3 +1,4 @@ +CATCH no_voucher flag_no_active_voucher 1 LOAD get_vouchers 0 MAP get_vouchers MOUT back 0 From 4e350aa25a6d22d4b0296d4bc22c77645f742159 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 5 Nov 2024 10:49:08 +0300 Subject: [PATCH 098/332] update test data file --- menutraversal_test/group_test.json | 6 +++--- menutraversal_test/test_setup.json | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index a219a6c..449c42f 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -54,7 +54,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" }, { "input": "1", @@ -95,7 +95,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" }, { "input": "1", @@ -141,7 +141,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" }, { "input": "1", diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 13166a4..3acb889 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -23,7 +23,7 @@ }, { "input": "1111", - "expectedContent": "The PIN is not a match. Try again\n1:retry\n9:Quit" + "expectedContent": "The PIN is not a match. Try again\n1:Retry\n9:Quit" }, { "input": "1", @@ -65,7 +65,7 @@ }, { "input": "000", - "expectedContent": "000 is not registered or invalid, please try again:\n1:retry\n9:Quit" + "expectedContent": "000 is not registered or invalid, please try again:\n1:Retry\n9:Quit" }, { "input": "1", @@ -77,7 +77,7 @@ }, { "input": "10000000", - "expectedContent": "Amount 10000000 is invalid, please try again:\n1:retry\n9:Quit" + "expectedContent": "Amount 10000000 is invalid, please try again:\n1:Retry\n9:Quit" }, { "input": "1", @@ -89,7 +89,7 @@ }, { "input": "1222", - "expectedContent": "Incorrect pin\n1:retry\n9:Quit" + "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" }, { "input": "1", @@ -140,7 +140,7 @@ }, { "input": "6", - "expectedContent": "Address: {public_key}\n9:Quit" + "expectedContent": "Address: {public_key}\n0:Back\n9:Quit" }, { "input": "9", From 308f3327d065b50d60257046438474078d98fa3b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 6 Nov 2024 17:48:50 +0300 Subject: [PATCH 099/332] use edit prefix on each profile edit information --- services/registration/edit_familyname_menu | 1 - services/registration/edit_familyname_menu_swa | 1 - services/registration/edit_name_menu | 1 - services/registration/edit_name_menu_swa | 1 - services/registration/enter_familyname | 1 - services/registration/enter_familyname.vis | 8 -------- services/registration/enter_familyname_swa | 1 - services/registration/enter_location | 1 - services/registration/enter_location.vis | 8 -------- services/registration/enter_location_swa | 1 - services/registration/enter_name | 1 - services/registration/enter_name.vis | 8 -------- services/registration/enter_name_swa | 1 - services/registration/enter_offerings | 1 - services/registration/enter_offerings.vis | 8 -------- services/registration/enter_offerings_swa | 1 - services/registration/enter_yob | 1 - services/registration/enter_yob.vis | 10 ---------- services/registration/enter_yob_swa | 1 - 19 files changed, 56 deletions(-) delete mode 100644 services/registration/edit_familyname_menu delete mode 100644 services/registration/edit_familyname_menu_swa delete mode 100644 services/registration/edit_name_menu delete mode 100644 services/registration/edit_name_menu_swa delete mode 100644 services/registration/enter_familyname delete mode 100644 services/registration/enter_familyname.vis delete mode 100644 services/registration/enter_familyname_swa delete mode 100644 services/registration/enter_location delete mode 100644 services/registration/enter_location.vis delete mode 100644 services/registration/enter_location_swa delete mode 100644 services/registration/enter_name delete mode 100644 services/registration/enter_name.vis delete mode 100644 services/registration/enter_name_swa delete mode 100644 services/registration/enter_offerings delete mode 100644 services/registration/enter_offerings.vis delete mode 100644 services/registration/enter_offerings_swa delete mode 100644 services/registration/enter_yob delete mode 100644 services/registration/enter_yob.vis delete mode 100644 services/registration/enter_yob_swa diff --git a/services/registration/edit_familyname_menu b/services/registration/edit_familyname_menu deleted file mode 100644 index 21a9033..0000000 --- a/services/registration/edit_familyname_menu +++ /dev/null @@ -1 +0,0 @@ -Edit family name \ No newline at end of file diff --git a/services/registration/edit_familyname_menu_swa b/services/registration/edit_familyname_menu_swa deleted file mode 100644 index 48a38b2..0000000 --- a/services/registration/edit_familyname_menu_swa +++ /dev/null @@ -1 +0,0 @@ -Weka jina la familia \ No newline at end of file diff --git a/services/registration/edit_name_menu b/services/registration/edit_name_menu deleted file mode 100644 index c7383b7..0000000 --- a/services/registration/edit_name_menu +++ /dev/null @@ -1 +0,0 @@ -Edit name \ No newline at end of file diff --git a/services/registration/edit_name_menu_swa b/services/registration/edit_name_menu_swa deleted file mode 100644 index 9395f7c..0000000 --- a/services/registration/edit_name_menu_swa +++ /dev/null @@ -1 +0,0 @@ -Weka jina \ No newline at end of file diff --git a/services/registration/enter_familyname b/services/registration/enter_familyname deleted file mode 100644 index 889915a..0000000 --- a/services/registration/enter_familyname +++ /dev/null @@ -1 +0,0 @@ -Enter family name: \ No newline at end of file diff --git a/services/registration/enter_familyname.vis b/services/registration/enter_familyname.vis deleted file mode 100644 index 5db4c17..0000000 --- a/services/registration/enter_familyname.vis +++ /dev/null @@ -1,8 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH update_familyname flag_allow_update 1 -MOUT back 0 -HALT -LOAD save_familyname 0 -RELOAD save_familyname -INCMP _ 0 -INCMP pin_entry * diff --git a/services/registration/enter_familyname_swa b/services/registration/enter_familyname_swa deleted file mode 100644 index 82f64cd..0000000 --- a/services/registration/enter_familyname_swa +++ /dev/null @@ -1 +0,0 @@ -Weka jina la familia diff --git a/services/registration/enter_location b/services/registration/enter_location deleted file mode 100644 index 675b835..0000000 --- a/services/registration/enter_location +++ /dev/null @@ -1 +0,0 @@ -Enter your location: \ No newline at end of file diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis deleted file mode 100644 index 8966872..0000000 --- a/services/registration/enter_location.vis +++ /dev/null @@ -1,8 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH update_location flag_allow_update 1 -MOUT back 0 -HALT -LOAD save_location 0 -RELOAD save_location -INCMP _ 0 -INCMP pin_entry * diff --git a/services/registration/enter_location_swa b/services/registration/enter_location_swa deleted file mode 100644 index 41682a2..0000000 --- a/services/registration/enter_location_swa +++ /dev/null @@ -1 +0,0 @@ -Weka eneo: \ No newline at end of file diff --git a/services/registration/enter_name b/services/registration/enter_name deleted file mode 100644 index c6851cf..0000000 --- a/services/registration/enter_name +++ /dev/null @@ -1 +0,0 @@ -Enter your first names: \ No newline at end of file diff --git a/services/registration/enter_name.vis b/services/registration/enter_name.vis deleted file mode 100644 index f853d0a..0000000 --- a/services/registration/enter_name.vis +++ /dev/null @@ -1,8 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH update_firstname flag_allow_update 1 -MOUT back 0 -HALT -LOAD save_firstname 0 -RELOAD save_firstname -INCMP _ 0 -INCMP pin_entry * diff --git a/services/registration/enter_name_swa b/services/registration/enter_name_swa deleted file mode 100644 index b600b90..0000000 --- a/services/registration/enter_name_swa +++ /dev/null @@ -1 +0,0 @@ -Weka majina yako ya kwanza: \ No newline at end of file diff --git a/services/registration/enter_offerings b/services/registration/enter_offerings deleted file mode 100644 index a9333ba..0000000 --- a/services/registration/enter_offerings +++ /dev/null @@ -1 +0,0 @@ -Enter the services or goods you offer: \ No newline at end of file diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis deleted file mode 100644 index 5cc7977..0000000 --- a/services/registration/enter_offerings.vis +++ /dev/null @@ -1,8 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH update_offerings flag_allow_update 1 -LOAD save_offerings 0 -MOUT back 0 -HALT -RELOAD save_offerings -INCMP _ 0 -INCMP pin_entry * diff --git a/services/registration/enter_offerings_swa b/services/registration/enter_offerings_swa deleted file mode 100644 index f37e125..0000000 --- a/services/registration/enter_offerings_swa +++ /dev/null @@ -1 +0,0 @@ -Weka unachouza \ No newline at end of file diff --git a/services/registration/enter_yob b/services/registration/enter_yob deleted file mode 100644 index 54e039e..0000000 --- a/services/registration/enter_yob +++ /dev/null @@ -1 +0,0 @@ -Enter your year of birth \ No newline at end of file diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis deleted file mode 100644 index c74aeed..0000000 --- a/services/registration/enter_yob.vis +++ /dev/null @@ -1,10 +0,0 @@ -CATCH incorrect_pin flag_incorrect_pin 1 -CATCH update_yob flag_allow_update 1 -MOUT back 0 -HALT -LOAD verify_yob 0 -CATCH incorrect_date_format flag_incorrect_date_format 1 -LOAD save_yob 0 -RELOAD save_yob -INCMP _ 0 -INCMP pin_entry * diff --git a/services/registration/enter_yob_swa b/services/registration/enter_yob_swa deleted file mode 100644 index 9bb272a..0000000 --- a/services/registration/enter_yob_swa +++ /dev/null @@ -1 +0,0 @@ -Weka mwaka wa kuzaliwa \ No newline at end of file From 29e1e912d79f2bdedb4be556570740f145fb6149 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 6 Nov 2024 17:52:22 +0300 Subject: [PATCH 100/332] use edit prefix on each profile edit node --- services/registration/edit_first_name.vis | 11 +++++++++++ services/registration/edit_first_name_menu_swa | 1 + services/registration/edit_location | 1 + services/registration/edit_location.vis | 10 ++++++++++ services/registration/edit_location_swa | 1 + services/registration/edit_offerings | 1 + services/registration/edit_offerings.vis | 10 ++++++++++ services/registration/edit_offerings_swa | 1 + services/registration/edit_profile.vis | 14 +++++++------- services/registration/edit_yob | 1 + services/registration/edit_yob.vis | 12 ++++++++++++ services/registration/edit_yob_swa | 1 + 12 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 services/registration/edit_first_name.vis create mode 100644 services/registration/edit_first_name_menu_swa create mode 100644 services/registration/edit_location create mode 100644 services/registration/edit_location.vis create mode 100644 services/registration/edit_location_swa create mode 100644 services/registration/edit_offerings create mode 100644 services/registration/edit_offerings.vis create mode 100644 services/registration/edit_offerings_swa create mode 100644 services/registration/edit_yob create mode 100644 services/registration/edit_yob.vis create mode 100644 services/registration/edit_yob_swa diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis new file mode 100644 index 0000000..b8dab5a --- /dev/null +++ b/services/registration/edit_first_name.vis @@ -0,0 +1,11 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_firstname flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info +MAP get_current_profile_info +MOUT back 0 +HALT +LOAD save_firstname 0 +RELOAD save_firstname +INCMP _ 0 +INCMP pin_entry * diff --git a/services/registration/edit_first_name_menu_swa b/services/registration/edit_first_name_menu_swa new file mode 100644 index 0000000..9395f7c --- /dev/null +++ b/services/registration/edit_first_name_menu_swa @@ -0,0 +1 @@ +Weka jina \ No newline at end of file diff --git a/services/registration/edit_location b/services/registration/edit_location new file mode 100644 index 0000000..675b835 --- /dev/null +++ b/services/registration/edit_location @@ -0,0 +1 @@ +Enter your location: \ No newline at end of file diff --git a/services/registration/edit_location.vis b/services/registration/edit_location.vis new file mode 100644 index 0000000..eaf248a --- /dev/null +++ b/services/registration/edit_location.vis @@ -0,0 +1,10 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_location flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info +MOUT back 0 +HALT +LOAD save_location 0 +RELOAD save_location +INCMP _ 0 +INCMP pin_entry * diff --git a/services/registration/edit_location_swa b/services/registration/edit_location_swa new file mode 100644 index 0000000..41682a2 --- /dev/null +++ b/services/registration/edit_location_swa @@ -0,0 +1 @@ +Weka eneo: \ No newline at end of file diff --git a/services/registration/edit_offerings b/services/registration/edit_offerings new file mode 100644 index 0000000..a9333ba --- /dev/null +++ b/services/registration/edit_offerings @@ -0,0 +1 @@ +Enter the services or goods you offer: \ No newline at end of file diff --git a/services/registration/edit_offerings.vis b/services/registration/edit_offerings.vis new file mode 100644 index 0000000..dad3193 --- /dev/null +++ b/services/registration/edit_offerings.vis @@ -0,0 +1,10 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_offerings flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info +LOAD save_offerings 0 +MOUT back 0 +HALT +RELOAD save_offerings +INCMP _ 0 +INCMP pin_entry * diff --git a/services/registration/edit_offerings_swa b/services/registration/edit_offerings_swa new file mode 100644 index 0000000..f37e125 --- /dev/null +++ b/services/registration/edit_offerings_swa @@ -0,0 +1 @@ +Weka unachouza \ No newline at end of file diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index 277f330..af20e0f 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -2,8 +2,8 @@ LOAD reset_account_authorized 16 RELOAD reset_account_authorized LOAD reset_allow_update 0 RELOAD reset_allow_update -MOUT edit_name 1 -MOUT edit_familyname 2 +MOUT edit_first_name 1 +MOUT edit_family_name 2 MOUT edit_gender 3 MOUT edit_yob 4 MOUT edit_location 5 @@ -12,10 +12,10 @@ MOUT view 7 MOUT back 0 HALT INCMP my_account 0 -INCMP enter_name 1 -INCMP enter_familyname 2 +INCMP edit_first_name 1 +INCMP edit_family_name 2 INCMP select_gender 3 -INCMP enter_yob 4 -INCMP enter_location 5 -INCMP enter_offerings 6 +INCMP edit_yob 4 +INCMP edit_location 5 +INCMP edit_offerings 6 INCMP view_profile 7 diff --git a/services/registration/edit_yob b/services/registration/edit_yob new file mode 100644 index 0000000..54e039e --- /dev/null +++ b/services/registration/edit_yob @@ -0,0 +1 @@ +Enter your year of birth \ No newline at end of file diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis new file mode 100644 index 0000000..3cb7bd0 --- /dev/null +++ b/services/registration/edit_yob.vis @@ -0,0 +1,12 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_yob flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info +MOUT back 0 +HALT +LOAD verify_yob 0 +CATCH incorrect_date_format flag_incorrect_date_format 1 +LOAD save_yob 0 +RELOAD save_yob +INCMP _ 0 +INCMP pin_entry * diff --git a/services/registration/edit_yob_swa b/services/registration/edit_yob_swa new file mode 100644 index 0000000..9bb272a --- /dev/null +++ b/services/registration/edit_yob_swa @@ -0,0 +1 @@ +Weka mwaka wa kuzaliwa \ No newline at end of file From ebdc7b200a572a6fef4ce9cbf13467554bf82dec Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 09:16:49 +0300 Subject: [PATCH 101/332] register handler for getting current profile information --- internal/handlers/handlerservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 7d8325c..7a1e912 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -115,6 +115,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) + ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo) return ussdHandlers, nil } From f66609bbaeb5a31e3574bcbcb0796e1cc7fabde2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 09:18:11 +0300 Subject: [PATCH 102/332] add helper for getting db key from string --- common/db.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/db.go b/common/db.go index 1992476..e90fd47 100644 --- a/common/db.go +++ b/common/db.go @@ -2,6 +2,7 @@ package common import ( "encoding/binary" + "errors" "git.defalsify.org/vise.git/logging" ) @@ -48,3 +49,23 @@ func PackKey(typ DataTyp, data []byte) []byte { v := typToBytes(typ) return append(v, data...) } + +func StringToDataTyp(str string) (DataTyp, error) { + switch str { + case "DATA_FIRST_NAME": + return DATA_FIRST_NAME, nil + case "DATA_FAMILY_NAME": + return DATA_FAMILY_NAME, nil + case "DATA_YOB": + return DATA_YOB, nil + case "DATA_LOCATION": + return DATA_LOCATION, nil + case "DATA_GENDER": + return DATA_GENDER, nil + case "DATA_OFFERINGS": + return DATA_OFFERINGS, nil + + default: + return 0, errors.New("invalid DataTyp string") + } +} From 3e258a35fa851a2c84fe9d4bfd998a94e7c8b2a7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 11:58:25 +0300 Subject: [PATCH 103/332] show currently set profile information --- services/registration/edit_family_name | 2 ++ services/registration/edit_family_name.vis | 10 ++++++++++ services/registration/edit_family_name_menu | 1 + services/registration/edit_family_name_menu_swa | 1 + services/registration/edit_family_name_swa | 2 ++ services/registration/edit_first_name | 2 ++ services/registration/edit_first_name_menu | 1 + services/registration/edit_first_name_swa | 2 ++ services/registration/edit_location | 1 + services/registration/edit_location_swa | 1 + services/registration/edit_offerings | 1 + services/registration/edit_offerings_swa | 1 + services/registration/edit_yob | 1 + services/registration/edit_yob.vis | 1 + services/registration/edit_yob_swa | 1 + services/registration/select_gender | 1 + services/registration/select_gender.vis | 6 ++---- 17 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 services/registration/edit_family_name create mode 100644 services/registration/edit_family_name.vis create mode 100644 services/registration/edit_family_name_menu create mode 100644 services/registration/edit_family_name_menu_swa create mode 100644 services/registration/edit_family_name_swa create mode 100644 services/registration/edit_first_name create mode 100644 services/registration/edit_first_name_menu create mode 100644 services/registration/edit_first_name_swa diff --git a/services/registration/edit_family_name b/services/registration/edit_family_name new file mode 100644 index 0000000..2170a7e --- /dev/null +++ b/services/registration/edit_family_name @@ -0,0 +1,2 @@ +Current familyname: {{.get_current_profile_info}} +Enter family name: \ No newline at end of file diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis new file mode 100644 index 0000000..1d71939 --- /dev/null +++ b/services/registration/edit_family_name.vis @@ -0,0 +1,10 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_familyname flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info +MOUT back 0 +HALT +LOAD save_familyname 0 +RELOAD save_familyname +INCMP _ 0 +INCMP pin_entry * diff --git a/services/registration/edit_family_name_menu b/services/registration/edit_family_name_menu new file mode 100644 index 0000000..21a9033 --- /dev/null +++ b/services/registration/edit_family_name_menu @@ -0,0 +1 @@ +Edit family name \ No newline at end of file diff --git a/services/registration/edit_family_name_menu_swa b/services/registration/edit_family_name_menu_swa new file mode 100644 index 0000000..48a38b2 --- /dev/null +++ b/services/registration/edit_family_name_menu_swa @@ -0,0 +1 @@ +Weka jina la familia \ No newline at end of file diff --git a/services/registration/edit_family_name_swa b/services/registration/edit_family_name_swa new file mode 100644 index 0000000..a1a1cab --- /dev/null +++ b/services/registration/edit_family_name_swa @@ -0,0 +1,2 @@ +Jina la familia la sasa: {{.get_current_profile_info}} +Weka jina la familia \ No newline at end of file diff --git a/services/registration/edit_first_name b/services/registration/edit_first_name new file mode 100644 index 0000000..3d141ee --- /dev/null +++ b/services/registration/edit_first_name @@ -0,0 +1,2 @@ +Current name: {{.get_current_profile_info}} +Enter your first names: \ No newline at end of file diff --git a/services/registration/edit_first_name_menu b/services/registration/edit_first_name_menu new file mode 100644 index 0000000..c7383b7 --- /dev/null +++ b/services/registration/edit_first_name_menu @@ -0,0 +1 @@ +Edit name \ No newline at end of file diff --git a/services/registration/edit_first_name_swa b/services/registration/edit_first_name_swa new file mode 100644 index 0000000..3fdd986 --- /dev/null +++ b/services/registration/edit_first_name_swa @@ -0,0 +1,2 @@ +Jina la kwanza la sasa {{.get_current_profile_info}} +Weka majina yako ya kwanza: \ No newline at end of file diff --git a/services/registration/edit_location b/services/registration/edit_location index 675b835..4e11d1a 100644 --- a/services/registration/edit_location +++ b/services/registration/edit_location @@ -1 +1,2 @@ +Current location: {{.get_current_profile_info}} Enter your location: \ No newline at end of file diff --git a/services/registration/edit_location_swa b/services/registration/edit_location_swa index 41682a2..0a3476e 100644 --- a/services/registration/edit_location_swa +++ b/services/registration/edit_location_swa @@ -1 +1,2 @@ +Eneo la sasa {{.get_current_profile_info}} Weka eneo: \ No newline at end of file diff --git a/services/registration/edit_offerings b/services/registration/edit_offerings index a9333ba..5bb0e7f 100644 --- a/services/registration/edit_offerings +++ b/services/registration/edit_offerings @@ -1 +1,2 @@ +Current offerings: {{.get_current_profile_info}} Enter the services or goods you offer: \ No newline at end of file diff --git a/services/registration/edit_offerings_swa b/services/registration/edit_offerings_swa index f37e125..cd81497 100644 --- a/services/registration/edit_offerings_swa +++ b/services/registration/edit_offerings_swa @@ -1 +1,2 @@ +Unachouza kwa sasa: {{.get_current_profile_info}} Weka unachouza \ No newline at end of file diff --git a/services/registration/edit_yob b/services/registration/edit_yob index 54e039e..105812b 100644 --- a/services/registration/edit_yob +++ b/services/registration/edit_yob @@ -1 +1,2 @@ +Current year of birth: {{.get_current_profile_info}} Enter your year of birth \ No newline at end of file diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis index 3cb7bd0..a260531 100644 --- a/services/registration/edit_yob.vis +++ b/services/registration/edit_yob.vis @@ -2,6 +2,7 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_yob flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info +MAP get_current_profile_info MOUT back 0 HALT LOAD verify_yob 0 diff --git a/services/registration/edit_yob_swa b/services/registration/edit_yob_swa index 9bb272a..e0b5708 100644 --- a/services/registration/edit_yob_swa +++ b/services/registration/edit_yob_swa @@ -1 +1,2 @@ +Mwaka wa sasa wa kuzaliwa {{.get_current_profile_info}} Weka mwaka wa kuzaliwa \ No newline at end of file diff --git a/services/registration/select_gender b/services/registration/select_gender index f8a6f47..22b9be9 100644 --- a/services/registration/select_gender +++ b/services/registration/select_gender @@ -1 +1,2 @@ +Current gender: {{.get_current_profile_info}} Select gender: \ No newline at end of file diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index 1082cef..c1a00f5 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -1,5 +1,7 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH profile_update_success flag_allow_update 1 +LOAD get_current_profile_info 0 +RELOAD get_current_profile_info MOUT male 1 MOUT female 2 MOUT unspecified 3 @@ -9,7 +11,3 @@ INCMP _ 0 INCMP set_male 1 INCMP set_female 2 INCMP set_unspecified 3 - - - - From 4e170b25e23fa396d9317e9aebdd64ea23593b2d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 11:58:58 +0300 Subject: [PATCH 104/332] show currently set profile information --- services/registration/select_gender_swa | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/select_gender_swa b/services/registration/select_gender_swa index 2b3a748..b077a0b 100644 --- a/services/registration/select_gender_swa +++ b/services/registration/select_gender_swa @@ -1 +1,2 @@ +Jinsia ya sasa {{.get_current_profile_info}} Chagua jinsia \ No newline at end of file From f3388aef31a122420aee22934c182f1b6c4b0302 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 11:59:12 +0300 Subject: [PATCH 105/332] use _ for back --- services/registration/incorrect_date_format.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/incorrect_date_format.vis b/services/registration/incorrect_date_format.vis index e94db5d..f4a8a2b 100644 --- a/services/registration/incorrect_date_format.vis +++ b/services/registration/incorrect_date_format.vis @@ -2,5 +2,5 @@ LOAD reset_incorrect_date_format 8 MOUT retry 1 MOUT quit 9 HALT -INCMP enter_yob 1 +INCMP _ 1 INCMP quit 9 From cc36ddcb6de9eeaec9ffb530e972658b533b5818 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 12:02:03 +0300 Subject: [PATCH 106/332] add handler for showing the currently set profile information --- internal/handlers/ussd/menuhandler.go | 89 +++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ca4af09..db710d2 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1153,6 +1153,95 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, nil } +func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + var profileInfo []byte + var err error + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + sm, _ := h.st.Where() + parts := strings.SplitN(sm, "_", 2) + filename := parts[1] + dbKeyStr := "DATA_" + strings.ToUpper(filename) + dbKey, err := common.StringToDataTyp(dbKeyStr) + + if err != nil { + return res, err + } + store := h.userdataStore + + switch dbKey { + case common.DATA_FIRST_NAME: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + case common.DATA_FAMILY_NAME: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + + case common.DATA_GENDER: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_GENDER) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + case common.DATA_YOB: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_YOB) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + + case common.DATA_LOCATION: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + case common.DATA_OFFERINGS: + profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) + if err != nil { + if db.IsNotFound(err) { + res.Content = "-" + break + } + return res, err + } + res.Content = string(profileInfo) + default: + break + } + + return res, nil +} + func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var defaultValue string From fc8915ea33a1dee0b0e3c4afc7cfa58dfee414e5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 16:35:58 +0300 Subject: [PATCH 107/332] add http logging --- remote/accountservice.go | 44 +++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 2e19de1..92fd9df 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -1,20 +1,26 @@ package remote import ( + "bytes" "context" "encoding/json" "errors" "io" + "log" "net/http" "net/url" + "os" - dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) var ( + DebugLogger = log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile) + InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime) + ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) ) type AccountServiceInterface interface { @@ -51,7 +57,7 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -79,7 +85,6 @@ func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (* return &balanceResult, err } - // CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. @@ -94,7 +99,7 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -118,7 +123,7 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -126,7 +131,6 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return r, nil } - // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. @@ -143,7 +147,7 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -151,7 +155,6 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return r, nil } - // VoucherData retrieves voucher metadata from the data indexer API endpoint. // Parameters: // - address: The voucher address. @@ -173,7 +176,7 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod } func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - var okResponse api.OKResponse + var okResponse api.OKResponse var errResponse api.ErrResponse req.Header.Set("Content-Type", "application/json") @@ -184,6 +187,7 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons } defer resp.Body.Close() + InfoLogger.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type")) body, err := io.ReadAll(resp.Body) if err != nil { return nil, err @@ -214,10 +218,30 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.CustodialAPIKey) + logRequestDetails(req, config.CustodialAPIKey) return doRequest(ctx, req, rcpt) } func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.DataAPIKey) + logRequestDetails(req, config.CustodialAPIKey) return doRequest(ctx, req, rcpt) } + +func logRequestDetails(req *http.Request, apiKey string) { + var bodyBytes []byte + contentType := req.Header.Get("Content-Type") + if req.Body != nil { + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + ErrorLogger.Printf("Error reading request body: %s", err) + return + } + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + } else { + bodyBytes = []byte("-") + } + + InfoLogger.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) + +} From 6ac9ac29d835dfe32c3f65e423798cbd7df5195b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 7 Nov 2024 16:41:08 +0300 Subject: [PATCH 108/332] add simple http logging --- remote/accountservice.go | 44 +++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 2e19de1..92fd9df 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -1,20 +1,26 @@ package remote import ( + "bytes" "context" "encoding/json" "errors" "io" + "log" "net/http" "net/url" + "os" - dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) var ( + DebugLogger = log.New(os.Stdout, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile) + InfoLogger = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime) + ErrorLogger = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) ) type AccountServiceInterface interface { @@ -51,7 +57,7 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -79,7 +85,6 @@ func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (* return &balanceResult, err } - // CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. @@ -94,7 +99,7 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -118,7 +123,7 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -126,7 +131,6 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return r, nil } - // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. @@ -143,7 +147,7 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -151,7 +155,6 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return r, nil } - // VoucherData retrieves voucher metadata from the data indexer API endpoint. // Parameters: // - address: The voucher address. @@ -173,7 +176,7 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod } func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - var okResponse api.OKResponse + var okResponse api.OKResponse var errResponse api.ErrResponse req.Header.Set("Content-Type", "application/json") @@ -184,6 +187,7 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons } defer resp.Body.Close() + InfoLogger.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type")) body, err := io.ReadAll(resp.Body) if err != nil { return nil, err @@ -214,10 +218,30 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.CustodialAPIKey) + logRequestDetails(req, config.CustodialAPIKey) return doRequest(ctx, req, rcpt) } func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.DataAPIKey) + logRequestDetails(req, config.CustodialAPIKey) return doRequest(ctx, req, rcpt) } + +func logRequestDetails(req *http.Request, apiKey string) { + var bodyBytes []byte + contentType := req.Header.Get("Content-Type") + if req.Body != nil { + bodyBytes, err := io.ReadAll(req.Body) + if err != nil { + ErrorLogger.Printf("Error reading request body: %s", err) + return + } + req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) + } else { + bodyBytes = []byte("-") + } + + InfoLogger.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) + +} From a11776e1b38c20092e4e8d9307993b51acf82cc4 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 7 Nov 2024 16:41:38 +0300 Subject: [PATCH 109/332] ignore .log files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ddccccf..b523c77 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ go.work* cmd/.state/ id_* *.gdbm +*.log From 9c972ffa6bb0faf851c4c6e607178e905e3937a9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 7 Nov 2024 16:46:12 +0300 Subject: [PATCH 110/332] add specific log files per binary --- cmd/africastalking/main.go | 42 ++++++++++++++++++++++++++++++++++++++ cmd/async/main.go | 19 ++++++++++++++++- cmd/http/main.go | 18 ++++++++++++++++ cmd/main.go | 18 ++++++++++++++++ remote/accountservice.go | 22 +++++++++++--------- 5 files changed, 108 insertions(+), 11 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index db66a2e..15a13ee 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -1,9 +1,13 @@ package main import ( + "bytes" "context" + "encoding/json" "flag" "fmt" + "io" + "log" "net/http" "os" "os/signal" @@ -27,10 +31,27 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + WarningLogger *log.Logger + InfoLogger *log.Logger + ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() + + logFile := "urdt-ussd-africastalking.log" + + file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + + InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + + // Inject into remote package + remote.InfoLogger = InfoLogger + remote.ErrorLogger = ErrorLogger } type atRequestParser struct{} @@ -38,9 +59,30 @@ type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { + ErrorLogger.Println("got an invalid request:", rq) return "", handlers.ErrInvalidRequest } + + // Capture body (if any) for logging + body, err := io.ReadAll(rqv.Body) + if err != nil { + ErrorLogger.Println("failed to read request body:", err) + return "", fmt.Errorf("failed to read request body: %v", err) + } + // Reset the body for further reading + rqv.Body = io.NopCloser(bytes.NewReader(body)) + + // Log the body as JSON + bodyLog := map[string]string{"body": string(body)} + logBytes, err := json.Marshal(bodyLog) + if err != nil { + ErrorLogger.Println("failed to marshal request body:", err) + } else { + InfoLogger.Println("Received request:", string(logBytes)) + } + if err := rqv.ParseForm(); err != nil { + ErrorLogger.Println("failed to parse form data: %v", err) return "", fmt.Errorf("failed to parse form data: %v", err) } diff --git a/cmd/async/main.go b/cmd/async/main.go index e4c94b0..0dd7c2c 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "log" "os" "os/signal" "path" @@ -23,12 +24,28 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + WarningLogger *log.Logger + InfoLogger *log.Logger + ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() -} + logFile := "urdt-ussd-async.log" + + file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + + InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + + // Inject into remote package + remote.InfoLogger = InfoLogger + remote.ErrorLogger = ErrorLogger +} type asyncRequestParser struct { sessionId string input []byte diff --git a/cmd/http/main.go b/cmd/http/main.go index 96e2688..ffb4109 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "log" "net/http" "os" "os/signal" @@ -26,10 +27,27 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + WarningLogger *log.Logger + InfoLogger *log.Logger + ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() + + logFile := "urdt-ussd-http.log" + + file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + + InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + + // Inject into remote package + remote.InfoLogger = InfoLogger + remote.ErrorLogger = ErrorLogger } func main() { diff --git a/cmd/main.go b/cmd/main.go index 9599eb7..a623cbe 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,6 +4,7 @@ import ( "context" "flag" "fmt" + "log" "os" "path" @@ -20,10 +21,27 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + WarningLogger *log.Logger + InfoLogger *log.Logger + ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() + + logFile := "urdt-ussd-cli.log" + + file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + log.Fatal(err) + } + + InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + + // Inject into remote package + remote.InfoLogger = InfoLogger + remote.ErrorLogger = ErrorLogger } func main() { diff --git a/remote/accountservice.go b/remote/accountservice.go index 2e19de1..6e222e7 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -5,16 +5,19 @@ import ( "encoding/json" "errors" "io" + "log" "net/http" "net/url" - dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/models" + "github.com/grassrootseconomics/eth-custodial/pkg/api" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) var ( + InfoLogger *log.Logger + ErrorLogger *log.Logger ) type AccountServiceInterface interface { @@ -51,7 +54,7 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -79,7 +82,6 @@ func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (* return &balanceResult, err } - // CreateAccount creates a new account in the custodial system. // Returns: // - *models.AccountResponse: A pointer to an AccountResponse struct containing the details of the created account. @@ -94,7 +96,7 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doCustodialRequest(ctx, req, &r) if err != nil { return nil, err } @@ -118,7 +120,7 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -126,7 +128,6 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return r, nil } - // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. @@ -143,7 +144,7 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doDataRequest(ctx, req, r) if err != nil { return nil, err } @@ -151,7 +152,6 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return r, nil } - // VoucherData retrieves voucher metadata from the data indexer API endpoint. // Parameters: // - address: The voucher address. @@ -173,9 +173,11 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod } func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - var okResponse api.OKResponse + var okResponse api.OKResponse var errResponse api.ErrResponse + InfoLogger.Printf("Outgoing request:", req.URL, req.Body) + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { From 1bcbb2079e81243e00198002cf8ae4f08303d570 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 8 Nov 2024 00:17:02 +0300 Subject: [PATCH 111/332] Removed unused variable --- remote/accountservice.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index ef0d4ae..ae1fab7 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -9,7 +9,6 @@ import ( "log" "net/http" "net/url" - "os" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/models" @@ -178,8 +177,6 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons var okResponse api.OKResponse var errResponse api.ErrResponse - InfoLogger.Printf("Outgoing request:", req.URL, req.Body) - req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { @@ -219,17 +216,17 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.CustodialAPIKey) - logRequestDetails(req, config.CustodialAPIKey) + logRequestDetails(req) return doRequest(ctx, req, rcpt) } func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { req.Header.Set("X-GE-KEY", config.DataAPIKey) - logRequestDetails(req, config.CustodialAPIKey) + logRequestDetails(req) return doRequest(ctx, req, rcpt) } -func logRequestDetails(req *http.Request, apiKey string) { +func logRequestDetails(req *http.Request) { var bodyBytes []byte contentType := req.Header.Get("Content-Type") if req.Body != nil { @@ -244,5 +241,4 @@ func logRequestDetails(req *http.Request, apiKey string) { } InfoLogger.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) - } From 64a7b492186384b4b6e6a71e99de76069f7bf880 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 8 Nov 2024 00:21:11 +0300 Subject: [PATCH 112/332] Remove unused Warning logger --- cmd/africastalking/main.go | 1 - cmd/async/main.go | 1 - cmd/http/main.go | 1 - cmd/main.go | 1 - 4 files changed, 4 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 15a13ee..7616473 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -31,7 +31,6 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger ) diff --git a/cmd/async/main.go b/cmd/async/main.go index 0dd7c2c..afc0bca 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -24,7 +24,6 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger ) diff --git a/cmd/http/main.go b/cmd/http/main.go index ffb4109..0565c45 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -27,7 +27,6 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger ) diff --git a/cmd/main.go b/cmd/main.go index a623cbe..857ccb9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -21,7 +21,6 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - WarningLogger *log.Logger InfoLogger *log.Logger ErrorLogger *log.Logger ) From dcd8fce59a81bca56961e94fb1625ce601e06e95 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 8 Nov 2024 10:07:06 +0300 Subject: [PATCH 113/332] add log on create account --- remote/accountservice.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index ae1fab7..e6a5556 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -96,9 +96,9 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes if err != nil { return nil, err } - _, err = doCustodialRequest(ctx, req, &r) if err != nil { + log.Printf("Failed to make custodial %s request to endpoint: %s with reason: %s", req.Method, req.URL, err.Error()) return nil, err } @@ -176,7 +176,6 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse - req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { From 574807d25465cd1919d4ccf9a8ecd36ee8a0b889 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 8 Nov 2024 16:52:19 +0300 Subject: [PATCH 114/332] set the africastalking POST route using env --- .env.example | 3 +++ cmd/africastalking/main.go | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index ab370a7..29b935e 100644 --- a/.env.example +++ b/.env.example @@ -16,3 +16,6 @@ CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ TRACK_URL=http://localhost:5003/api/v2/account/status + +#AfricasTalking USSD POST endpoint +AT_ENDPOINT=/ussd/africastalking diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 7616473..bae83b9 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -29,10 +29,10 @@ import ( ) var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") - InfoLogger *log.Logger - ErrorLogger *log.Logger + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") + InfoLogger *log.Logger + ErrorLogger *log.Logger ) func init() { @@ -197,9 +197,13 @@ func main() { rp := &atRequestParser{} bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl) sh := httpserver.NewATSessionHandler(bsh) + + mux := http.NewServeMux() + mux.Handle(initializers.GetEnv("AT_ENDPOINT", "/"), sh) + s := &http.Server{ Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))), - Handler: sh, + Handler: mux, } s.RegisterOnShutdown(sh.Shutdown) From f4f95b32926cc6c1407505c33964902c16cfb6ee Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 8 Nov 2024 17:15:27 +0300 Subject: [PATCH 115/332] add some spacing --- services/registration/edit_family_name | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/edit_family_name b/services/registration/edit_family_name index 2170a7e..1d637be 100644 --- a/services/registration/edit_family_name +++ b/services/registration/edit_family_name @@ -1,2 +1,2 @@ -Current familyname: {{.get_current_profile_info}} +Current family name: {{.get_current_profile_info}} Enter family name: \ No newline at end of file From 68d162854614e9ae8b111f0a5d9bc440172b74f5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 8 Nov 2024 17:19:41 +0300 Subject: [PATCH 116/332] replace - with: Not provided --- internal/handlers/ussd/menuhandler.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index db710d2..d59b1e9 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1177,7 +1177,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err @@ -1187,7 +1187,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err @@ -1198,7 +1198,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_GENDER) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err @@ -1208,7 +1208,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_YOB) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err @@ -1219,7 +1219,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err @@ -1229,7 +1229,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) if err != nil { if db.IsNotFound(err) { - res.Content = "-" + res.Content = "Not provided" break } return res, err From 9ebfb643aaa67d8379fb5051e688de9f47c94795 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 8 Nov 2024 17:28:20 +0300 Subject: [PATCH 117/332] remove unused import --- remote/accountservice.go | 1 - 1 file changed, 1 deletion(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 3a51e45..e6a5556 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -9,7 +9,6 @@ import ( "log" "net/http" "net/url" - "os" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/models" From 1d07d7fb1d014acf084e0834f7be0bec3c7091be Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 8 Nov 2024 20:35:17 +0300 Subject: [PATCH 118/332] updated README --- README.md | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 35ef7f1..493dd96 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,91 @@ -# ussd +# URDT USSD service -> USSD +This is a USSD service built using the [go-vise](https://github.com/nolash/go-vise) engine. -USSD service. +## Prerequisites +### 1. [go-vise](https://github.com/nolash/go-vise) + +Set up `go-vise` by cloning the repository into a separate directory. The main upstream repository is hosted at: `https://git.defalsify.org/vise.git` +``` +git clone https://git.defalsify.org/vise.git +``` + +## Setup +1. Clone the ussd repo in its own directory + + ``` + git clone https://git.grassecon.net/urdt/ussd.git + ``` + +2. Navigate to the project directory. +3. Enter the `services/registration` subfolder: + ``` + cd services/registration + ``` +4. make the .bin files from the .vis files + ``` + make VISE_PATH=/var/path/to/your/go-vise -B + ``` +5. Return to the project root (`cd ../..`) +6. Run the USSD menu + ``` + go run cmd/main.go -session-id=0712345678 + ``` +## Running the different binaries +1. ### CLI: + ``` + go run cmd/main.go -session-id=0712345678 + ``` +2. ### Africastalking: + ``` + go run cmd/africastalking/main.go + ``` +3. ### Async: + ``` + go run cmd/async/main.go + ``` +4. ### Http: + ``` + go run cmd/http/main.go + ``` + +## Flags +Below are the supported flags: + +1. `-session-id`: + + Specifies the session ID. (CLI only). + + Default: `075xx2123`. + + Example: + ``` + go run cmd/main.go -session-id=0712345678 + ``` + +2. `-d`: + + Enables engine debug output. + + Default: `false`. + + Example: + ``` + go run cmd/main.go -session-id=0712345678 -d + ``` + +3. `-db`: + + Specifies the database type. + + Default: `gdbm`. + + Example: + ``` + go run cmd/main.go -session-id=0712345678 -d -db=postgres + ``` + + >Note: If using `-db=postgres`, ensure PostgreSQL is running with the connection details specified in your `.env` file. ## License From 9af7b775a78cddcf0ba0ea33517daf2d0fb3542a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 11 Nov 2024 16:32:17 +0300 Subject: [PATCH 119/332] log directly to the terminal --- cmd/africastalking/main.go | 26 +++++--------------------- cmd/async/main.go | 18 +----------------- cmd/http/main.go | 17 ----------------- cmd/main.go | 17 ----------------- remote/accountservice.go | 13 ++++--------- 5 files changed, 10 insertions(+), 81 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index bae83b9..5a523e7 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -31,26 +31,10 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - InfoLogger *log.Logger - ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() - - logFile := "urdt-ussd-africastalking.log" - - file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - log.Fatal(err) - } - - InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) - - // Inject into remote package - remote.InfoLogger = InfoLogger - remote.ErrorLogger = ErrorLogger } type atRequestParser struct{} @@ -58,14 +42,14 @@ type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { - ErrorLogger.Println("got an invalid request:", rq) + log.Println("got an invalid request:", rq) return "", handlers.ErrInvalidRequest } // Capture body (if any) for logging body, err := io.ReadAll(rqv.Body) if err != nil { - ErrorLogger.Println("failed to read request body:", err) + log.Println("failed to read request body:", err) return "", fmt.Errorf("failed to read request body: %v", err) } // Reset the body for further reading @@ -75,13 +59,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { bodyLog := map[string]string{"body": string(body)} logBytes, err := json.Marshal(bodyLog) if err != nil { - ErrorLogger.Println("failed to marshal request body:", err) + log.Println("failed to marshal request body:", err) } else { - InfoLogger.Println("Received request:", string(logBytes)) + log.Println("Received request:", string(logBytes)) } if err := rqv.ParseForm(); err != nil { - ErrorLogger.Println("failed to parse form data: %v", err) + log.Println("failed to parse form data: %v", err) return "", fmt.Errorf("failed to parse form data: %v", err) } diff --git a/cmd/async/main.go b/cmd/async/main.go index afc0bca..e4c94b0 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -4,7 +4,6 @@ import ( "context" "flag" "fmt" - "log" "os" "os/signal" "path" @@ -24,27 +23,12 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - InfoLogger *log.Logger - ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() - - logFile := "urdt-ussd-async.log" - - file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - log.Fatal(err) - } - - InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) - - // Inject into remote package - remote.InfoLogger = InfoLogger - remote.ErrorLogger = ErrorLogger } + type asyncRequestParser struct { sessionId string input []byte diff --git a/cmd/http/main.go b/cmd/http/main.go index 0565c45..96e2688 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -4,7 +4,6 @@ import ( "context" "flag" "fmt" - "log" "net/http" "os" "os/signal" @@ -27,26 +26,10 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - InfoLogger *log.Logger - ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() - - logFile := "urdt-ussd-http.log" - - file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - log.Fatal(err) - } - - InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) - - // Inject into remote package - remote.InfoLogger = InfoLogger - remote.ErrorLogger = ErrorLogger } func main() { diff --git a/cmd/main.go b/cmd/main.go index 857ccb9..9599eb7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -4,7 +4,6 @@ import ( "context" "flag" "fmt" - "log" "os" "path" @@ -21,26 +20,10 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") - InfoLogger *log.Logger - ErrorLogger *log.Logger ) func init() { initializers.LoadEnvVariables() - - logFile := "urdt-ussd-cli.log" - - file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) - if err != nil { - log.Fatal(err) - } - - InfoLogger = log.New(file, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - ErrorLogger = log.New(file, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) - - // Inject into remote package - remote.InfoLogger = InfoLogger - remote.ErrorLogger = ErrorLogger } func main() { diff --git a/remote/accountservice.go b/remote/accountservice.go index e6a5556..388fd9c 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -16,11 +16,6 @@ import ( dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) -var ( - InfoLogger *log.Logger - ErrorLogger *log.Logger -) - type AccountServiceInterface interface { CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) CreateAccount(ctx context.Context) (*models.AccountResult, error) @@ -98,7 +93,6 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes } _, err = doCustodialRequest(ctx, req, &r) if err != nil { - log.Printf("Failed to make custodial %s request to endpoint: %s with reason: %s", req.Method, req.URL, err.Error()) return nil, err } @@ -179,12 +173,13 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { + log.Printf("Failed to make %s request to endpoint: %s with reason: %s", req.Method, req.URL, err.Error()) errResponse.Description = err.Error() return nil, err } defer resp.Body.Close() - InfoLogger.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type")) + log.Printf("Received response for %s: Status Code: %d | Content-Type: %s", req.URL, resp.StatusCode, resp.Header.Get("Content-Type")) body, err := io.ReadAll(resp.Body) if err != nil { return nil, err @@ -231,7 +226,7 @@ func logRequestDetails(req *http.Request) { if req.Body != nil { bodyBytes, err := io.ReadAll(req.Body) if err != nil { - ErrorLogger.Printf("Error reading request body: %s", err) + log.Printf("Error reading request body: %s", err) return } req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) @@ -239,5 +234,5 @@ func logRequestDetails(req *http.Request) { bodyBytes = []byte("-") } - InfoLogger.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) + log.Printf("URL: %s | Content-Type: %s | Method: %s| Request Body: %s", req.URL, contentType, req.Method, string(bodyBytes)) } From abdb17640b22e6fbdd6e860f1169557acafb60e2 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 12 Nov 2024 10:36:08 +0300 Subject: [PATCH 120/332] add terminal logs --- internal/handlers/ussd/menuhandler.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ca4af09..6fcb865 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -239,6 +239,7 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt } temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { + return res, err } if bytes.Equal(temporaryPin, input) { @@ -291,6 +292,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt store := h.userdataStore err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) return res, err } @@ -310,10 +312,12 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { + logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) return res, err } @@ -331,6 +335,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt store := h.userdataStore temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { + logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } if bytes.Equal(temporaryPin, input) { @@ -340,6 +345,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt } err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) return res, err } return res, nil @@ -374,6 +380,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) return res, err } @@ -406,11 +413,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) return res, err } } @@ -465,11 +474,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) return res, err } } @@ -495,11 +506,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_LOCATION, "value", temporaryLocation, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) return res, err } } @@ -525,11 +538,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_GENDER, "value", gender, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) return res, err } } @@ -556,11 +571,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { + logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) return res, err } } From 09f61eb64d07c852011e54b1eeec02374e3922a5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 13 Nov 2024 15:19:45 +0300 Subject: [PATCH 121/332] log all errors from the hander --- internal/handlers/ussd/menuhandler.go | 50 ++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 6fcb865..58df521 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "log" "path" "regexp" "strconv" @@ -161,6 +162,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r languageSetFlag, err := h.flagManager.GetFlag("flag_language_set") if err != nil { + log.Printf("Error: %s", err) return res, err } res.FlagSet = append(res.FlagSet, languageSetFlag) @@ -198,7 +200,6 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r } res.FlagSet = append(res.FlagSet, flag_account_created) return nil - } // CreateAccount checks if any account exists on the JSON data file, and if not @@ -218,10 +219,12 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") err = h.createAccountNoExist(ctx, sessionId, &res) if err != nil { + log.Printf("Error: %s", err) return res, err } } } + return res, nil } @@ -235,11 +238,13 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt store := h.userdataStore blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { + log.Printf("Error: %s", err) return res, err } temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { + log.Printf("Error: %s", err) return res, err } if bytes.Equal(temporaryPin, input) { @@ -293,6 +298,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) + log.Printf("Error: %s", err) return res, err } @@ -313,11 +319,13 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input if err != nil { logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) + log.Printf("Error: %s", err) return res, err } err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) + log.Printf("Error: %s", err) return res, err } @@ -336,6 +344,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) + log.Printf("Error: %s", err) return res, err } if bytes.Equal(temporaryPin, input) { @@ -346,6 +355,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) + log.Printf("Error: %s", err) return res, err } return res, nil @@ -368,6 +378,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte store := h.userdataStore temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { + log.Printf("Error: %s", err) return res, err } if bytes.Equal(input, temporaryPin) { @@ -381,6 +392,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) + log.Printf("Error: %s", err) return res, err } @@ -414,12 +426,14 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) + log.Printf("Error: %s", err) return res, err } } @@ -446,11 +460,13 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { + log.Printf("Error: %s", err) return res, err } } @@ -475,12 +491,14 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err) + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) + log.Printf("Error: %s", err) return res, err } } @@ -507,12 +525,14 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_LOCATION, "value", temporaryLocation, "error", err) + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) + log.Printf("Error: %s", err) return res, err } } @@ -539,12 +559,14 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_GENDER, "value", gender, "error", err) + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) + log.Printf("Error: %s", err) return res, err } } @@ -572,12 +594,14 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) + log.Printf("Error: %s", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) + log.Printf("Error: %s", err) return res, err } } @@ -643,6 +667,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res store := h.userdataStore AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) if err != nil { + log.Printf("Error: %s", err) return res, err } if len(input) == 4 { @@ -690,16 +715,19 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { + log.Printf("Error: %s", err) return res, err } r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) + log.Printf("Error: %s", err) return res, err } res.FlagReset = append(res.FlagReset, flag_api_error) if !ok { + log.Printf("Error: %s", err) return res, err } if r.Active { @@ -800,11 +828,13 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, nil } + log.Printf("Error: %s", err) return res, err } activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -828,6 +858,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -860,10 +891,12 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) } blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { + log.Printf("Error: %s", err) return res, err } temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) if err != nil { + log.Printf("Error: %s", err) return res, err } err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) @@ -902,6 +935,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } else { + log.Printf("Error: %s", err) return res, err } } @@ -1009,6 +1043,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1034,10 +1069,12 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // retrieve the active balance activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { + log.Printf("Error: %s", err) return res, err } balanceValue, err = strconv.ParseFloat(string(activeBal), 64) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1060,6 +1097,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) formattedAmount := fmt.Sprintf("%.2f", inputAmount) err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1126,6 +1164,7 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res // retrieve the active symbol activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1163,6 +1202,7 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1265,6 +1305,7 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by if db.IsNotFound(err) { publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1289,17 +1330,20 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by // set the active symbol err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) if err != nil { + log.Printf("Error: %s", err) return res, err } // set the active balance err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) if err != nil { + log.Printf("Error: %s", err) return res, err } return res, nil } + log.Printf("Error: %s", err) return res, err } @@ -1355,6 +1399,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) // Read vouchers from the store voucherData, err := h.prefixDb.Get(ctx, []byte("sym")) if err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1390,6 +1435,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { + log.Printf("Error: %s", err) return res, err } @@ -1411,11 +1457,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re // Get temporary data tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { + log.Printf("Error: %s", err) return res, err } // Set as active and clear temporary data if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { + log.Printf("Error: %s", err) return res, err } From 047bf0e12ee59764e21829acb3aa7fe932f79694 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 13 Nov 2024 16:15:37 +0300 Subject: [PATCH 122/332] updated the env example --- .env.example | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 29b935e..d392c9b 100644 --- a/.env.example +++ b/.env.example @@ -2,6 +2,9 @@ PORT=7123 HOST=127.0.0.1 +#AfricasTalking USSD POST endpoint +AT_ENDPOINT=/ussd/africastalking + #PostgreSQL DB_HOST=localhost DB_USER=postgres @@ -12,10 +15,7 @@ DB_SSLMODE=disable DB_TIMEZONE=Africa/Nairobi #External API Calls -CREATE_ACCOUNT_URL=http://localhost:5003/api/v2/account/create -TRACK_STATUS_URL=https://custodial.sarafu.africa/api/track/ -BALANCE_URL=https://custodial.sarafu.africa/api/account/status/ -TRACK_URL=http://localhost:5003/api/v2/account/status - -#AfricasTalking USSD POST endpoint -AT_ENDPOINT=/ussd/africastalking +CUSTODIAL_URL_BASE=http://localhost:5003 +CUSTODIAL_BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr +DATA_URL_BASE=http://localhost:5006 +DATA_BEARER_TOKEN=eyJeSIsIRcCI6IXVCJ.yJwdWJsaLZXkiOiIwrrrrrr From d7232a53efab7acd5d9ba3bc378ccd42defc7add Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 13 Nov 2024 16:16:32 +0300 Subject: [PATCH 123/332] use the bearer token --- config/config.go | 40 ++++++++++++++++++++-------------------- remote/accountservice.go | 4 ++-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/config/config.go b/config/config.go index fbf518b..c4445eb 100644 --- a/config/config.go +++ b/config/config.go @@ -7,30 +7,30 @@ import ( ) const ( - createAccountPath = "/api/v2/account/create" - trackStatusPath = "/api/track" - balancePathPrefix = "/api/account" - trackPath = "/api/v2/account/status" - voucherHoldingsPathPrefix = "/api/v1/holdings" + createAccountPath = "/api/v2/account/create" + trackStatusPath = "/api/track" + balancePathPrefix = "/api/account" + trackPath = "/api/v2/account/status" + voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" - voucherDataPathPrefix = "/api/v1/token" + voucherDataPathPrefix = "/api/v1/token" ) var ( - custodialURLBase string - dataURLBase string - CustodialAPIKey string - DataAPIKey string + custodialURLBase string + dataURLBase string + CustodialBearerToken string + DataBearerToken string ) var ( - CreateAccountURL string - TrackStatusURL string - BalanceURL string - TrackURL string - VoucherHoldingsURL string - VoucherTransfersURL string - VoucherDataURL string + CreateAccountURL string + TrackStatusURL string + BalanceURL string + TrackURL string + VoucherHoldingsURL string + VoucherTransfersURL string + VoucherDataURL string ) func setBase() error { @@ -38,8 +38,8 @@ func setBase() error { custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") - CustodialAPIKey = initializers.GetEnv("CUSTODIAL_API_KEY", "xd") - DataAPIKey = initializers.GetEnv("DATA_API_KEY", "xd") + CustodialBearerToken = initializers.GetEnv("CUSTODIAL_BEARER_TOKEN", "") + DataBearerToken = initializers.GetEnv("DATA_BEARER_TOKEN", "") _, err = url.JoinPath(custodialURLBase, "/foo") if err != nil { @@ -58,7 +58,7 @@ func LoadConfig() error { if err != nil { return err } - CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath) + CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath) TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) diff --git a/remote/accountservice.go b/remote/accountservice.go index 388fd9c..19914cd 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -209,13 +209,13 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons } func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - req.Header.Set("X-GE-KEY", config.CustodialAPIKey) + req.Header.Set("Authorization", "Bearer "+config.CustodialBearerToken) logRequestDetails(req) return doRequest(ctx, req, rcpt) } func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - req.Header.Set("X-GE-KEY", config.DataAPIKey) + req.Header.Set("Authorization", "Bearer "+config.DataBearerToken) logRequestDetails(req) return doRequest(ctx, req, rcpt) } From b6b3ef83a466ce6ad7e357697e717dc96859d4d0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 13 Nov 2024 18:02:02 +0300 Subject: [PATCH 124/332] updated the comment to match the functionality --- common/vouchers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/vouchers.go b/common/vouchers.go index 2fed043..dd8852e 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -135,7 +135,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str return data, nil } -// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore. +// UpdateVoucherData sets the active voucher data in the DataStore. func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { logg.TraceCtxf(ctx, "dtal", "data", data) // Active voucher data entries From f9f25d898b0ec435c7fef41a1ab32277c1914478 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 13 Nov 2024 19:00:27 +0300 Subject: [PATCH 125/332] use logg --- internal/handlers/ussd/menuhandler.go | 135 +++++++++++--------------- 1 file changed, 58 insertions(+), 77 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 58df521..1836833 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "log" "path" "regexp" "strconv" @@ -162,7 +161,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r languageSetFlag, err := h.flagManager.GetFlag("flag_language_set") if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "Error setting the languageSetFlag", "error", err) return res, err } res.FlagSet = append(res.FlagSet, languageSetFlag) @@ -216,10 +215,10 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) if err != nil { if db.IsNotFound(err) { - logg.Printf(logging.LVL_INFO, "Creating an account because it doesn't exist") + logg.InfoCtxf(ctx, "Creating an account because it doesn't exist") err = h.createAccountNoExist(ctx, sessionId, &res) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed on createAccountNoExist", "error", err) return res, err } } @@ -238,13 +237,12 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt store := h.userdataStore blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { - - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } if bytes.Equal(temporaryPin, input) { @@ -297,8 +295,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt store := h.userdataStore err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(accountPIN)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryAccountPIN entry with", "key", common.DATA_TEMPORARY_VALUE, "value", accountPIN, "error", err) return res, err } @@ -316,16 +313,14 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input } temporaryPin := string(input) blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } + err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) return res, err } @@ -343,8 +338,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt store := h.userdataStore temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { - logg.ErrorCtxf(ctx, "failed to read entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } if bytes.Equal(temporaryPin, input) { @@ -354,8 +348,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt } err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) return res, err } return res, nil @@ -378,7 +371,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte store := h.userdataStore temporaryPin, err := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } if bytes.Equal(input, temporaryPin) { @@ -391,8 +384,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) return res, err } @@ -425,15 +417,13 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", common.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryFirstName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) return res, err } } @@ -460,16 +450,17 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(temporaryFamilyName)) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write familyName entry with", "key", common.DATA_FAMILY_NAME, "value", temporaryFamilyName, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryFamilyName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", familyName, "error", err) return res, err } } + return res, nil } @@ -490,15 +481,13 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(temporaryYob)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write yob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryYob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) return res, err } } @@ -524,15 +513,13 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(temporaryLocation)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_LOCATION, "value", temporaryLocation, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write location entry with", "key", common.DATA_LOCATION, "value", temporaryLocation, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryLocation entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) return res, err } } @@ -558,15 +545,13 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(temporaryGender)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_GENDER, "value", gender, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write gender entry with", "key", common.DATA_GENDER, "value", gender, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryGender entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) return res, err } } @@ -593,15 +578,13 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(temporaryOfferings)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write offerings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) return res, err } } else { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write temporaryOfferings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) return res, err } } @@ -612,9 +595,7 @@ 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) { var res resource.Result - flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") - res.FlagReset = append(res.FlagReset, flag_allow_update) return res, nil } @@ -631,7 +612,6 @@ func (h *Handlers) ResetValidPin(ctx context.Context, sym string, input []byte) func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") - res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } @@ -667,7 +647,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res store := h.userdataStore AccountPin, err := store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read AccountPin entry with", "key", common.DATA_ACCOUNT_PIN, "error", err) return res, err } if len(input) == 4 { @@ -715,21 +695,19 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) return res, err } - r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) + r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed on TrackAccountStatus", err) return res, err } + res.FlagReset = append(res.FlagReset, flag_api_error) - if !ok { - log.Printf("Error: %s", err) - return res, err - } + if r.Active { res.FlagSet = append(res.FlagSet, flag_account_success) res.FlagReset = append(res.FlagReset, flag_account_pending) @@ -737,6 +715,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b res.FlagReset = append(res.FlagReset, flag_account_success) res.FlagSet = append(res.FlagSet, flag_account_pending) } + return res, nil } @@ -828,13 +807,13 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, nil } - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", common.DATA_ACTIVE_SYM, "error", err) return res, err } activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", common.DATA_ACTIVE_BAL, "error", err) return res, err } @@ -858,7 +837,7 @@ func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) return res, err } @@ -891,18 +870,19 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) } blockedPhonenumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read blockedPhonenumber entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } temporaryPin, err := store.ReadEntry(ctx, string(blockedPhonenumber), common.DATA_TEMPORARY_VALUE) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { return res, nil } + return res, nil } @@ -931,11 +911,11 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input } if err != nil { if db.IsNotFound(err) { - logg.Printf(logging.LVL_INFO, "Invalid or unregistered number") + logg.InfoCtxf(ctx, "Invalid or unregistered number") res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } else { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "Error on ValidateBlockedNumber", "error", err) return res, err } } @@ -1043,7 +1023,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", common.DATA_ACTIVE_BAL, "error", err) return res, err } @@ -1069,12 +1049,12 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // retrieve the active balance activeBal, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_BAL) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeBal entry with", "key", common.DATA_ACTIVE_BAL, "error", err) return res, err } balanceValue, err = strconv.ParseFloat(string(activeBal), 64) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "Failed to convert the activeBal to a float", "error", err) return res, err } @@ -1097,11 +1077,11 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) formattedAmount := fmt.Sprintf("%.2f", inputAmount) err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(formattedAmount)) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write amount entry with", "key", common.DATA_AMOUNT, "value", formattedAmount, "error", err) return res, err } - res.Content = fmt.Sprintf("%s", formattedAmount) + res.Content = formattedAmount return res, nil } @@ -1164,7 +1144,7 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res // retrieve the active symbol activeSym, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", common.DATA_ACTIVE_SYM, "error", err) return res, err } @@ -1202,7 +1182,7 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "Failed to set the flag_account_authorized", "error", err) return res, err } @@ -1305,7 +1285,7 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by if db.IsNotFound(err) { publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) return res, err } @@ -1330,20 +1310,20 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by // set the active symbol err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write defaultSym entry with", "key", common.DATA_ACTIVE_SYM, "value", defaultSym, "error", err) return res, err } // set the active balance err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", common.DATA_ACTIVE_BAL, "value", defaultBal, "error", err) return res, err } return res, nil } - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", common.DATA_ACTIVE_SYM, "error", err) return res, err } @@ -1364,7 +1344,8 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { - return res, nil + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err } // Fetch vouchers from the API using the public key @@ -1399,7 +1380,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) // Read vouchers from the store voucherData, err := h.prefixDb.Get(ctx, []byte("sym")) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err) return res, err } @@ -1435,7 +1416,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } if err := common.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err) return res, err } @@ -1457,13 +1438,13 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re // Get temporary data tempData, err := common.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId) if err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err) return res, err } // Set as active and clear temporary data if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil { - log.Printf("Error: %s", err) + logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err) return res, err } From f97ad2a262ad326aeaf485787a490397013bbe64 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 14 Nov 2024 10:25:42 +0300 Subject: [PATCH 126/332] use 1 for retry --- services/registration/api_failure.vis | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/registration/api_failure.vis b/services/registration/api_failure.vis index e045355..78318f2 100644 --- a/services/registration/api_failure.vis +++ b/services/registration/api_failure.vis @@ -1,5 +1,6 @@ -MOUT retry 0 +MOUT retry 1 MOUT quit 9 HALT -INCMP _ 0 +INCMP _ 1 INCMP quit 9 + From f9e51618c5eb598632f3283da00a2132b7b122ef Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 14 Nov 2024 10:29:22 +0300 Subject: [PATCH 127/332] remove extra line --- services/registration/api_failure.vis | 1 - 1 file changed, 1 deletion(-) diff --git a/services/registration/api_failure.vis b/services/registration/api_failure.vis index 78318f2..37b3deb 100644 --- a/services/registration/api_failure.vis +++ b/services/registration/api_failure.vis @@ -3,4 +3,3 @@ MOUT quit 9 HALT INCMP _ 1 INCMP quit 9 - From 94d2e8203f9cb34c2451a320ac2514eb3469bfbe Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 14 Nov 2024 10:53:20 +0300 Subject: [PATCH 128/332] reload verify yob --- services/registration/edit_yob.vis | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis index a260531..6a5abe0 100644 --- a/services/registration/edit_yob.vis +++ b/services/registration/edit_yob.vis @@ -5,7 +5,8 @@ RELOAD get_current_profile_info MAP get_current_profile_info MOUT back 0 HALT -LOAD verify_yob 0 +LOAD verify_yob 6 +RELOAD verify_yob CATCH incorrect_date_format flag_incorrect_date_format 1 LOAD save_yob 0 RELOAD save_yob From 381e581e8ef855852d6aa909248c1934189f7b72 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 14 Nov 2024 11:00:12 +0300 Subject: [PATCH 129/332] add terminal logs --- internal/handlers/ussd/menuhandler.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 7d1db1e..31d0e30 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1217,6 +1217,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read first name entry with", "key", "error", common.DATA_FIRST_NAME, err) return res, err } res.Content = string(profileInfo) @@ -1227,6 +1228,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read family name entry with", "key", "error", common.DATA_FAMILY_NAME, err) return res, err } res.Content = string(profileInfo) @@ -1238,6 +1240,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read gender entry with", "key", "error", common.DATA_GENDER, err) return res, err } res.Content = string(profileInfo) @@ -1248,6 +1251,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read year of birth(yob) entry with", "key", "error", common.DATA_YOB, err) return res, err } res.Content = string(profileInfo) @@ -1259,6 +1263,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read location entry with", "key", "error", common.DATA_LOCATION, err) return res, err } res.Content = string(profileInfo) @@ -1269,6 +1274,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input res.Content = "Not provided" break } + logg.ErrorCtxf(ctx, "Failed to read offerings entry with", "key", "error", common.DATA_OFFERINGS, err) return res, err } res.Content = string(profileInfo) From 93df6a6a08d5f6c21ee67c1848a4336a0f318465 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 14:59:39 +0300 Subject: [PATCH 130/332] validate recipients and invite valid ones --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 58 +++++++++++++++++++-- services/registration/invalid_recipient | 2 +- services/registration/invalid_recipient_swa | 2 +- services/registration/invite_menu | 1 + services/registration/invite_menu_swa | 1 + services/registration/invite_recipient | 1 + services/registration/invite_recipient.vis | 8 +++ services/registration/invite_recipient_swa | 1 + services/registration/invite_result.vis | 2 + services/registration/locale/swa/default.po | 6 +++ services/registration/send.vis | 2 + 12 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 services/registration/invite_menu create mode 100644 services/registration/invite_menu_swa create mode 100644 services/registration/invite_recipient create mode 100644 services/registration/invite_recipient.vis create mode 100644 services/registration/invite_recipient_swa create mode 100644 services/registration/invite_result.vis diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 7a1e912..358d492 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -80,6 +80,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance) ls.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient) ls.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset) + ls.DbRs.AddLocalFunc("invite_valid_recipient", ussdHandlers.InviteValidRecipient) ls.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount) ls.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount) ls.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 31d0e30..138ebca 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -929,7 +929,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input // ValidateRecipient validates that the given input is a valid phone number. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error + store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -939,18 +939,41 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by recipient := string(input) flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") + flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") if recipient != "0" { - // mimic invalid number check - if recipient == "000" { + if !isValidPhoneNumber(recipient) { res.FlagSet = append(res.FlagSet, flag_invalid_recipient) res.Content = recipient return res, nil } - store := h.userdataStore - err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) + + publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) if err != nil { + if db.IsNotFound(err) { + logg.InfoCtxf(ctx, "Unregistered number") + + // save the recipient as the temporaryInvitedNumber + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(recipient)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryInvitedNumber entry with", "key", common.DATA_TEMPORARY_VALUE, "value", recipient, "error", err) + return res, err + } + + res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) + res.Content = recipient + + return res, nil + } + + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err) return res, nil } } @@ -987,6 +1010,31 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt return res, nil } +// InviteValidRecipient sends an invitation to the valid phone number. +func (h *Handlers) InviteValidRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + store := h.userdataStore + + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + code := codeFromCtx(ctx) + l := gotext.NewLocale(translationDir, code) + l.AddDomain("default") + + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + + // TODO + // send an invitation SMS + // if successful + // res.Content = l.Get("Your invitation to %s to join Sarafu Network has been sent.", string(recipient)) + + res.Content = l.Get("Your invite request for %s to Sarafu Network failed. Please try again later.", string(recipient)) + return res, nil +} + // ResetTransactionAmount resets the transaction amount and invalid flag func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result diff --git a/services/registration/invalid_recipient b/services/registration/invalid_recipient index 0be78bd..d9fcb1d 100644 --- a/services/registration/invalid_recipient +++ b/services/registration/invalid_recipient @@ -1 +1 @@ -{{.validate_recipient}} is not registered or invalid, please try again: \ No newline at end of file +{{.validate_recipient}} is invalid, please try again: \ No newline at end of file diff --git a/services/registration/invalid_recipient_swa b/services/registration/invalid_recipient_swa index 39e7804..13dda97 100644 --- a/services/registration/invalid_recipient_swa +++ b/services/registration/invalid_recipient_swa @@ -1 +1 @@ -{{.validate_recipient}} haijasajiliwa au sio sahihi, tafadhali weka tena: \ No newline at end of file +{{.validate_recipient}} sio sahihi, tafadhali weka tena: \ No newline at end of file diff --git a/services/registration/invite_menu b/services/registration/invite_menu new file mode 100644 index 0000000..e44862a --- /dev/null +++ b/services/registration/invite_menu @@ -0,0 +1 @@ +Invite to Sarafu Network \ No newline at end of file diff --git a/services/registration/invite_menu_swa b/services/registration/invite_menu_swa new file mode 100644 index 0000000..48c0ddf --- /dev/null +++ b/services/registration/invite_menu_swa @@ -0,0 +1 @@ +Karibisha kwa matandao wa Sarafu \ No newline at end of file diff --git a/services/registration/invite_recipient b/services/registration/invite_recipient new file mode 100644 index 0000000..aa3438d --- /dev/null +++ b/services/registration/invite_recipient @@ -0,0 +1 @@ +{{.validate_recipient}} is not registered, please try again: \ No newline at end of file diff --git a/services/registration/invite_recipient.vis b/services/registration/invite_recipient.vis new file mode 100644 index 0000000..1a4845f --- /dev/null +++ b/services/registration/invite_recipient.vis @@ -0,0 +1,8 @@ +MAP validate_recipient +MOUT retry 1 +MOUT invite 2 +MOUT quit 9 +HALT +INCMP _ 1 +INCMP invite_result 2 +INCMP quit 9 diff --git a/services/registration/invite_recipient_swa b/services/registration/invite_recipient_swa new file mode 100644 index 0000000..30cf599 --- /dev/null +++ b/services/registration/invite_recipient_swa @@ -0,0 +1 @@ +{{.validate_recipient}} haijasajiliwa, tafadhali weka tena: \ No newline at end of file diff --git a/services/registration/invite_result.vis b/services/registration/invite_result.vis new file mode 100644 index 0000000..5f31749 --- /dev/null +++ b/services/registration/invite_result.vis @@ -0,0 +1,2 @@ +LOAD invite_valid_recipient 0 +HALT diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index ba9a9bb..3e5213c 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -12,3 +12,9 @@ msgstr "Kwa usaidizi zaidi,piga: 0757628885" msgid "Balance: %s\n" msgstr "Salio: %s\n" + +msid "Your invite request for %s to Sarafu Network failed. Please try again later." +msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu halikufaulu. Tafadhali jaribu tena baadaye." + +msgid "Your invitation to %s to join Sarafu Network has been sent." +msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu limetumwa." diff --git a/services/registration/send.vis b/services/registration/send.vis index 0ff0927..8928725 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -1,9 +1,11 @@ LOAD transaction_reset 0 +RELOAD transaction_reset CATCH no_voucher flag_no_active_voucher 1 MOUT back 0 HALT LOAD validate_recipient 20 RELOAD validate_recipient CATCH invalid_recipient flag_invalid_recipient 1 +CATCH invite_recipient flag_invalid_recipient_with_invite 1 INCMP _ 0 INCMP amount * From 9d2d01e3e21e311195ec77bc370bf7bb8b29c21d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 17:30:13 +0300 Subject: [PATCH 131/332] save the recipient number in DATA_TEMPORARY_VALUE --- internal/handlers/ussd/menuhandler.go | 24 +++++++++++------------- services/registration/amount.vis | 4 ++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 138ebca..055a314 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -929,6 +929,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input // ValidateRecipient validates that the given input is a valid phone number. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + var err error store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) @@ -949,18 +950,18 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, nil } + // save the recipient as the temporaryRecipient + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(recipient)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryRecipient entry with", "key", common.DATA_TEMPORARY_VALUE, "value", recipient, "error", err) + return res, err + } + publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) if err != nil { if db.IsNotFound(err) { logg.InfoCtxf(ctx, "Unregistered number") - // save the recipient as the temporaryInvitedNumber - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(recipient)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryInvitedNumber entry with", "key", common.DATA_TEMPORARY_VALUE, "value", recipient, "error", err) - return res, err - } - res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) res.Content = recipient @@ -1133,7 +1134,7 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) return res, nil } -// GetRecipient returns the transaction recipient from the gdbm. +// GetRecipient returns the transaction recipient phone number from the gdbm. func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1142,7 +1143,7 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, fmt.Errorf("missing session") } store := h.userdataStore - recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) res.Content = string(recipient) @@ -1219,11 +1220,8 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] // TODO // Use the amount, recipient and sender to call the API and initialize the transaction store := h.userdataStore - amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) - - recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) - + recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) diff --git a/services/registration/amount.vis b/services/registration/amount.vis index 82e1fd4..2266160 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -6,10 +6,10 @@ MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount -CATCH api_failure flag_api_call_error 1 +CATCH api_failure flag_api_call_error 1 CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 -LOAD get_recipient 12 +LOAD get_recipient 0 LOAD get_sender 64 LOAD get_amount 32 INCMP transaction_pin * From f3e3badff68044c04ac96badd8b33b49582bd1d4 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 17:31:17 +0300 Subject: [PATCH 132/332] save the entire voucher data when setting the default voucher --- internal/handlers/ussd/menuhandler.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 055a314..68580c2 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1447,6 +1447,8 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by firstVoucher := vouchersResp[0] defaultSym := firstVoucher.TokenSymbol defaultBal := firstVoucher.Balance + defaultDec := firstVoucher.TokenDecimals + defaultAddr := firstVoucher.ContractAddress // set the active symbol err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) @@ -1460,6 +1462,18 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", common.DATA_ACTIVE_BAL, "value", defaultBal, "error", err) return res, err } + // set the active decimals + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_DECIMAL, []byte(defaultDec)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write defaultDec entry with", "key", common.DATA_ACTIVE_DECIMAL, "value", defaultDec, "error", err) + return res, err + } + // set the active contract address + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS, []byte(defaultAddr)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write defaultAddr entry with", "key", common.DATA_ACTIVE_ADDRESS, "value", defaultAddr, "error", err) + return res, err + } return res, nil } From fabcccfa6048d5eeec89a094d5bec2b7c6807778 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 19:35:44 +0300 Subject: [PATCH 133/332] added the tokenTransfer url --- config/config.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/config.go b/config/config.go index c4445eb..0d39e6e 100644 --- a/config/config.go +++ b/config/config.go @@ -11,6 +11,7 @@ const ( trackStatusPath = "/api/track" balancePathPrefix = "/api/account" trackPath = "/api/v2/account/status" + tokenTransferPrefix = "/api/v2/token/transfer" voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" voucherDataPathPrefix = "/api/v1/token" @@ -28,6 +29,7 @@ var ( TrackStatusURL string BalanceURL string TrackURL string + TokenTransferURL string VoucherHoldingsURL string VoucherTransfersURL string VoucherDataURL string @@ -62,6 +64,7 @@ func LoadConfig() error { TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath) BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix) TrackURL, _ = url.JoinPath(custodialURLBase, trackPath) + TokenTransferURL, _ = url.JoinPath(custodialURLBase, tokenTransferPrefix) VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) From c10e1a6a1ba6dde3d2bbcf674403faaad6d746ea Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 19:36:30 +0300 Subject: [PATCH 134/332] added the TokenTransferResponse model --- models/token_transfer_response.go | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 models/token_transfer_response.go diff --git a/models/token_transfer_response.go b/models/token_transfer_response.go new file mode 100644 index 0000000..b4d6dc3 --- /dev/null +++ b/models/token_transfer_response.go @@ -0,0 +1,5 @@ +package models + +type TokenTransferResponse struct { + TrackingId string `json:"trackingId"` +} From c34906cb1f9fe4ec602a10819b4e7cef6110c539 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 20:02:48 +0300 Subject: [PATCH 135/332] added the TokenTransfer functionality and mocks --- internal/testutil/mocks/servicemock.go | 8 +++-- .../testservice/TestAccountService.go | 18 ++++++---- remote/accountservice.go | 36 +++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 76803ba..a25f1ae 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -28,7 +28,6 @@ func (m *MockAccountService) TrackAccountStatus(ctx context.Context, trackingId return args.Get(0).(*models.TrackStatusResult), args.Error(1) } - func (m *MockAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { args := m.Called(publicKey) return args.Get(0).([]dataserviceapi.TokenHoldings), args.Error(1) @@ -39,7 +38,12 @@ func (m *MockAccountService) FetchTransactions(ctx context.Context, publicKey st return args.Get(0).([]dataserviceapi.Last10TxResponse), args.Error(1) } -func(m MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { +func (m *MockAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { args := m.Called(address) return args.Get(0).(*models.VoucherDataResult), args.Error(1) } + +func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) { + args := m.Called() + return args.Get(0).(*models.TokenTransferResponse), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 8752d6f..7c486f3 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -12,14 +12,14 @@ type TestAccountService struct { } func (tas *TestAccountService) CreateAccount(ctx context.Context) (*models.AccountResult, error) { - return &models.AccountResult { + return &models.AccountResult{ TrackingId: "075ccc86-f6ef-4d33-97d5-e91cfb37aa0d", - PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", + PublicKey: "0x623EFAFa8868df4B934dd12a8B26CB3Dd75A7AdD", }, nil } func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey string) (*models.BalanceResult, error) { - balanceResponse := &models.BalanceResult { + balanceResponse := &models.BalanceResult{ Balance: "0.003 CELO", Nonce: json.Number("0"), } @@ -27,7 +27,7 @@ func (tas *TestAccountService) CheckBalance(ctx context.Context, publicKey strin } func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey string) (*models.TrackStatusResult, error) { - return &models.TrackStatusResult { + return &models.TrackStatusResult{ Active: true, }, nil } @@ -40,13 +40,19 @@ func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey stri TokenDecimals: "6", Balance: "2745987", }, - }, nil + }, nil } func (tas *TestAccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { return []dataserviceapi.Last10TxResponse{}, nil } -func(m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { +func (m TestAccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { return &models.VoucherDataResult{}, nil } + +func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) { + return &models.TokenTransferResponse{ + TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af", + }, nil +} diff --git a/remote/accountservice.go b/remote/accountservice.go index 19914cd..3af1ab1 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -23,6 +23,7 @@ type AccountServiceInterface interface { FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) + TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) } type AccountService struct { @@ -167,6 +168,41 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod return &voucherDataResult, err } +// TokenTransfer creates a new token transfer in the custodial system. +// Returns: +// - *models.TokenTransferResponse: A pointer to an TokenTransferResponse struct containing the trackingId. +// 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) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) { + var r models.TokenTransferResponse + + // Create request payload + payload := map[string]string{ + "amount": amount, + "from": from, + "to": to, + "tokenAddress": tokenAddress, + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return nil, err + } + + // Create a new request + req, err := http.NewRequest("POST", config.TokenTransferURL, bytes.NewBuffer(payloadBytes)) + if err != nil { + return nil, err + } + _, err = doCustodialRequest(ctx, req, &r) + if err != nil { + return nil, err + } + + return &r, nil +} + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse From 7985b20200f34f75c494aaef4b7ca7730ffe32f0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 20:09:50 +0300 Subject: [PATCH 136/332] added message string for failed transaction --- services/registration/locale/swa/default.po | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index 3e5213c..f933dde 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -18,3 +18,6 @@ msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu halikufaulu. Tafadhali msgid "Your invitation to %s to join Sarafu Network has been sent." msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu limetumwa." + +msgid "Your request failed. Please try again later." +msgstr "Ombi lako halikufaulu. Tafadhali jaribu tena baadaye." From 5773305785e9a761a8751965a961bae410961620 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 20:10:48 +0300 Subject: [PATCH 137/332] parse data and initialize a transaction --- internal/handlers/ussd/menuhandler.go | 49 ++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 68580c2..00c6ad7 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "math/big" "path" "regexp" "strconv" @@ -702,7 +703,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b r, err := h.accountService.TrackAccountStatus(ctx, string(publicKey)) if err != nil { res.FlagSet = append(res.FlagSet, flag_api_error) - logg.ErrorCtxf(ctx, "failed on TrackAccountStatus", err) + logg.ErrorCtxf(ctx, "failed on TrackAccountStatus", "error", err) return res, err } @@ -1214,24 +1215,56 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, fmt.Errorf("missing session") } + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + account_authorized_flag, _ := h.flagManager.GetFlag("flag_account_authorized") + code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - // TODO - // Use the amount, recipient and sender to call the API and initialize the transaction + store := h.userdataStore - amount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) - recipient, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) + recipientNumber, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) - res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipient), string(amount), string(activeSym), string(sessionId)) + storedAmount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) + fromAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + toAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) + activeDecimal, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_DECIMAL) + tokenAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS) - account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") + // Parse tokendecimal + tokenDecimal, err := strconv.Atoi(string(activeDecimal)) if err != nil { - logg.ErrorCtxf(ctx, "Failed to set the flag_account_authorized", "error", err) return res, err } + // Parse amount and scale it + amount, _, err := big.ParseFloat(string(storedAmount), 10, 0, big.ToZero) + if err != nil { + return res, err + } + + multiplier := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenDecimal)), nil)) + finalAmount := new(big.Float).Mul(amount, multiplier) + + // Convert finalAmount to a string + finalAmountStr := new(big.Int) + finalAmount.Int(finalAmountStr) + + // Call TokenTransfer + r, err := h.accountService.TokenTransfer(ctx, finalAmountStr.String(), string(fromAddress), string(toAddress), string(tokenAddress)) + if err != nil { + res.Content = l.Get("Your request failed. Please try again later.") + res.FlagSet = append(res.FlagSet, flag_api_error) + logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err) + return res, err + } + + trackingId := r.TrackingId + logg.InfoCtxf(ctx, "TokenTransfer", "trackingId", trackingId) + + res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipientNumber), string(storedAmount), string(activeSym), string(sessionId)) + res.FlagReset = append(res.FlagReset, account_authorized_flag) return res, nil } From de5ecc5fe76fb9d0c07be99d21f1bb315d5a3cdb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 14 Nov 2024 21:21:04 +0300 Subject: [PATCH 138/332] add the tokens functionality to the common package --- common/tokens.go | 54 +++++++++++++++++++++++++++ internal/handlers/ussd/menuhandler.go | 49 +++++++++--------------- 2 files changed, 71 insertions(+), 32 deletions(-) create mode 100644 common/tokens.go diff --git a/common/tokens.go b/common/tokens.go new file mode 100644 index 0000000..727cbea --- /dev/null +++ b/common/tokens.go @@ -0,0 +1,54 @@ +package common + +import ( + "context" + "math/big" + "strconv" +) + +func ParseAndScaleAmount(storedAmount, activeDecimal []byte) (string, error) { + // Parse token decimal + tokenDecimal, err := strconv.Atoi(string(activeDecimal)) + if err != nil { + + return "", err + } + + // Parse amount + amount, _, err := big.ParseFloat(string(storedAmount), 10, 0, big.ToZero) + if err != nil { + return "", err + } + + // Scale the amount + multiplier := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenDecimal)), nil)) + finalAmount := new(big.Float).Mul(amount, multiplier) + + // Convert finalAmount to a string + finalAmountStr := new(big.Int) + finalAmount.Int(finalAmountStr) + + return finalAmountStr.String(), nil +} + +func ReadTransactionData(ctx context.Context, store DataStore, sessionId string) (map[DataTyp][]byte, error) { + dataKeys := []DataTyp{ + DATA_TEMPORARY_VALUE, + DATA_ACTIVE_SYM, + DATA_AMOUNT, + DATA_PUBLIC_KEY, + DATA_RECIPIENT, + DATA_ACTIVE_DECIMAL, + DATA_ACTIVE_ADDRESS, + } + + data := make(map[DataTyp][]byte) + for _, key := range dataKeys { + value, err := store.ReadEntry(ctx, sessionId, key) + if err != nil { + return nil, err + } + data[key] = value + } + return data, nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 00c6ad7..d2411bd 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "math/big" "path" "regexp" "strconv" @@ -1176,7 +1175,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res return res, fmt.Errorf("missing session") } - res.Content = string(sessionId) + res.Content = sessionId return res, nil } @@ -1205,8 +1204,7 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res return res, nil } -// InitiateTransaction returns a confirmation and resets the transaction data -// on the gdbm store. +// InitiateTransaction calls the TokenTransfer and returns a confirmation based on the result func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1215,57 +1213,44 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, fmt.Errorf("missing session") } - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - account_authorized_flag, _ := h.flagManager.GetFlag("flag_account_authorized") + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - store := h.userdataStore - recipientNumber, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) - activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) - - storedAmount, _ := store.ReadEntry(ctx, sessionId, common.DATA_AMOUNT) - fromAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) - toAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_RECIPIENT) - activeDecimal, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_DECIMAL) - tokenAddress, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS) - - // Parse tokendecimal - tokenDecimal, err := strconv.Atoi(string(activeDecimal)) + data, err := common.ReadTransactionData(ctx, h.userdataStore, sessionId) if err != nil { return res, err } - // Parse amount and scale it - amount, _, err := big.ParseFloat(string(storedAmount), 10, 0, big.ToZero) + finalAmountStr, err := common.ParseAndScaleAmount(data[common.DATA_AMOUNT], data[common.DATA_ACTIVE_DECIMAL]) if err != nil { return res, err } - multiplier := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(tokenDecimal)), nil)) - finalAmount := new(big.Float).Mul(amount, multiplier) - - // Convert finalAmount to a string - finalAmountStr := new(big.Int) - finalAmount.Int(finalAmountStr) - // Call TokenTransfer - r, err := h.accountService.TokenTransfer(ctx, finalAmountStr.String(), string(fromAddress), string(toAddress), string(tokenAddress)) + r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, string(data[common.DATA_PUBLIC_KEY]), string(data[common.DATA_RECIPIENT]), string(data[common.DATA_ACTIVE_ADDRESS])) if err != nil { - res.Content = l.Get("Your request failed. Please try again later.") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") res.FlagSet = append(res.FlagSet, flag_api_error) + res.Content = l.Get("Your request failed. Please try again later.") logg.ErrorCtxf(ctx, "failed on TokenTransfer", "error", err) - return res, err + return res, nil } trackingId := r.TrackingId logg.InfoCtxf(ctx, "TokenTransfer", "trackingId", trackingId) - res.Content = l.Get("Your request has been sent. %s will receive %s %s from %s.", string(recipientNumber), string(storedAmount), string(activeSym), string(sessionId)) + res.Content = l.Get( + "Your request has been sent. %s will receive %s %s from %s.", + string(data[common.DATA_TEMPORARY_VALUE]), + string(data[common.DATA_AMOUNT]), + string(data[common.DATA_ACTIVE_SYM]), + sessionId, + ) - res.FlagReset = append(res.FlagReset, account_authorized_flag) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } From cb13b0929192f6f5ad91304d5f52736111bd214f Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 08:42:44 +0300 Subject: [PATCH 139/332] remove commented code --- common/vouchers.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/common/vouchers.go b/common/vouchers.go index dd8852e..54ace28 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -37,15 +37,6 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { return data } -//func StoreVouchers(db storage.PrefixDb, data VoucherMetadata) { -// value, err := db.Put(ctx, []byte(key)) -// if err != nil { -// return nil, fmt.Errorf("failed to get %s: %v", key, err) -// } -// data[key] = string(value) -// } -//} - // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []string{"sym", "bal", "deci", "addr"} From 1a0b4deab3aea25f7dbedb352fd6cf00af0b6476 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 09:20:19 +0300 Subject: [PATCH 140/332] added the TransactionData struct to organiza the data --- common/tokens.go | 63 +++++++++++++++++++-------- internal/handlers/ussd/menuhandler.go | 10 ++--- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/common/tokens.go b/common/tokens.go index 727cbea..466f370 100644 --- a/common/tokens.go +++ b/common/tokens.go @@ -2,20 +2,32 @@ package common import ( "context" + "errors" "math/big" + "reflect" "strconv" ) -func ParseAndScaleAmount(storedAmount, activeDecimal []byte) (string, error) { +type TransactionData struct { + TemporaryValue string + ActiveSym string + Amount string + PublicKey string + Recipient string + ActiveDecimal string + ActiveAddress string +} + +func ParseAndScaleAmount(storedAmount, activeDecimal string) (string, error) { // Parse token decimal - tokenDecimal, err := strconv.Atoi(string(activeDecimal)) + tokenDecimal, err := strconv.Atoi(activeDecimal) if err != nil { return "", err } // Parse amount - amount, _, err := big.ParseFloat(string(storedAmount), 10, 0, big.ToZero) + amount, _, err := big.ParseFloat(storedAmount, 10, 0, big.ToZero) if err != nil { return "", err } @@ -31,24 +43,39 @@ func ParseAndScaleAmount(storedAmount, activeDecimal []byte) (string, error) { return finalAmountStr.String(), nil } -func ReadTransactionData(ctx context.Context, store DataStore, sessionId string) (map[DataTyp][]byte, error) { - dataKeys := []DataTyp{ - DATA_TEMPORARY_VALUE, - DATA_ACTIVE_SYM, - DATA_AMOUNT, - DATA_PUBLIC_KEY, - DATA_RECIPIENT, - DATA_ACTIVE_DECIMAL, - DATA_ACTIVE_ADDRESS, +func ReadTransactionData(ctx context.Context, store DataStore, sessionId string) (TransactionData, error) { + data := TransactionData{} + fieldToKey := map[string]DataTyp{ + "TemporaryValue": DATA_TEMPORARY_VALUE, + "ActiveSym": DATA_ACTIVE_SYM, + "Amount": DATA_AMOUNT, + "PublicKey": DATA_PUBLIC_KEY, + "Recipient": DATA_RECIPIENT, + "ActiveDecimal": DATA_ACTIVE_DECIMAL, + "ActiveAddress": DATA_ACTIVE_ADDRESS, } - data := make(map[DataTyp][]byte) - for _, key := range dataKeys { - value, err := store.ReadEntry(ctx, sessionId, key) - if err != nil { - return nil, err + v := reflect.ValueOf(&data).Elem() + for fieldName, key := range fieldToKey { + field := v.FieldByName(fieldName) + if !field.IsValid() || !field.CanSet() { + return data, errors.New("invalid struct field: " + fieldName) } - data[key] = value + + value, err := readStringEntry(ctx, store, sessionId, key) + if err != nil { + return data, err + } + field.SetString(value) } + return data, nil } + +func readStringEntry(ctx context.Context, store DataStore, sessionId string, key DataTyp) (string, error) { + entry, err := store.ReadEntry(ctx, sessionId, key) + if err != nil { + return "", err + } + return string(entry), nil +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d2411bd..0b9360f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1224,13 +1224,13 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, err } - finalAmountStr, err := common.ParseAndScaleAmount(data[common.DATA_AMOUNT], data[common.DATA_ACTIVE_DECIMAL]) + finalAmountStr, err := common.ParseAndScaleAmount(data.Amount, data.ActiveDecimal) if err != nil { return res, err } // Call TokenTransfer - r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, string(data[common.DATA_PUBLIC_KEY]), string(data[common.DATA_RECIPIENT]), string(data[common.DATA_ACTIVE_ADDRESS])) + r, err := h.accountService.TokenTransfer(ctx, finalAmountStr, data.PublicKey, data.Recipient, data.ActiveAddress) if err != nil { flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") res.FlagSet = append(res.FlagSet, flag_api_error) @@ -1244,9 +1244,9 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] res.Content = l.Get( "Your request has been sent. %s will receive %s %s from %s.", - string(data[common.DATA_TEMPORARY_VALUE]), - string(data[common.DATA_AMOUNT]), - string(data[common.DATA_ACTIVE_SYM]), + data.TemporaryValue, + data.Amount, + data.ActiveSym, sessionId, ) From 222d801ecc454d60c3eec4f31148ff2763b3aff5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 11:49:47 +0300 Subject: [PATCH 141/332] added TestParseAndScaleAmount --- common/tokens_test.go | 88 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 common/tokens_test.go diff --git a/common/tokens_test.go b/common/tokens_test.go new file mode 100644 index 0000000..5634ab9 --- /dev/null +++ b/common/tokens_test.go @@ -0,0 +1,88 @@ +package common + +import ( + "testing" +) + +func TestParseAndScaleAmount(t *testing.T) { + tests := []struct { + name string + amount string + decimals string + want string + expectError bool + }{ + { + name: "whole number", + amount: "123", + decimals: "2", + want: "12300", + expectError: false, + }, + { + name: "decimal number", + amount: "123.45", + decimals: "2", + want: "12345", + expectError: false, + }, + { + name: "zero decimals", + amount: "123.45", + decimals: "0", + want: "123", + expectError: false, + }, + { + name: "large number", + amount: "1000000.01", + decimals: "6", + want: "1000000010000", + expectError: false, + }, + { + name: "invalid amount", + amount: "abc", + decimals: "2", + want: "", + expectError: true, + }, + { + name: "invalid decimals", + amount: "123.45", + decimals: "abc", + want: "", + expectError: true, + }, + { + name: "zero amount", + amount: "0", + decimals: "2", + want: "0", + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseAndScaleAmount(tt.amount, tt.decimals) + + // Check error cases + if tt.expectError { + if err == nil { + t.Errorf("ParseAndScaleAmount(%q, %q) expected error, got nil", tt.amount, tt.decimals) + } + return + } + + if err != nil { + t.Errorf("ParseAndScaleAmount(%q, %q) unexpected error: %v", tt.amount, tt.decimals, err) + return + } + + if got != tt.want { + t.Errorf("ParseAndScaleAmount(%q, %q) = %v, want %v", tt.amount, tt.decimals, got, tt.want) + } + }) + } +} From 36846c2587c830332d3d528af62c2e828b6b0a52 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 12:13:09 +0300 Subject: [PATCH 142/332] added TestReadTransactionData --- common/tokens_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/common/tokens_test.go b/common/tokens_test.go index 5634ab9..06bd552 100644 --- a/common/tokens_test.go +++ b/common/tokens_test.go @@ -2,6 +2,8 @@ package common import ( "testing" + + "github.com/alecthomas/assert/v2" ) func TestParseAndScaleAmount(t *testing.T) { @@ -86,3 +88,42 @@ func TestParseAndScaleAmount(t *testing.T) { }) } } + +func TestReadTransactionData(t *testing.T) { + sessionId := "session123" + publicKey := "0X13242618721" + ctx, store := InitializeTestDb(t) + + // Test transaction data + transactionData := map[DataTyp]string{ + DATA_TEMPORARY_VALUE: "0712345678", + DATA_ACTIVE_SYM: "SRF", + DATA_AMOUNT: "1000000", + DATA_PUBLIC_KEY: publicKey, + DATA_RECIPIENT: "0x41c188d63Qa", + DATA_ACTIVE_DECIMAL: "6", + DATA_ACTIVE_ADDRESS: "0xd4c288865Ce", + } + + // Store the data + for key, value := range transactionData { + if err := store.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil { + t.Fatal(err) + } + } + + expectedResult := TransactionData{ + TemporaryValue: "0712345678", + ActiveSym: "SRF", + Amount: "1000000", + PublicKey: publicKey, + Recipient: "0x41c188d63Qa", + ActiveDecimal: "6", + ActiveAddress: "0xd4c288865Ce", + } + + data, err := ReadTransactionData(ctx, store, sessionId) + + assert.NoError(t, err) + assert.Equal(t, expectedResult, data) +} From 1a77092ccbfd25d02d66c801cdb9166efcb889d1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 13:12:30 +0300 Subject: [PATCH 143/332] updated the menuhandler tests --- internal/handlers/ussd/menuhandler_test.go | 97 ++++++++++++++++------ 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index f4c9f7c..ff6f0ad 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -586,9 +586,9 @@ func TestGetRecipient(t *testing.T) { ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) - recepient := "0xcasgatweksalw1018221" + recepient := "0712345678" - err := store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recepient)) + err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(recepient)) if err != nil { t.Fatal(err) } @@ -1226,32 +1226,41 @@ func TestInitiateTransaction(t *testing.T) { } tests := []struct { - name string - input []byte - Recipient []byte - Amount []byte - ActiveSym []byte - status string - expectedResult resource.Result + name string + TemporaryValue []byte + ActiveSym []byte + StoredAmount []byte + TransferAmount string + PublicKey []byte + Recipient []byte + ActiveDecimal []byte + ActiveAddress []byte + TransferResponse *models.TokenTransferResponse + expectedResult resource.Result }{ { - name: "Test initiate transaction", - Amount: []byte("0.002"), - ActiveSym: []byte("SRF"), - Recipient: []byte("0x12415ass27192"), + name: "Test initiate transaction", + TemporaryValue: []byte("0711223344"), + ActiveSym: []byte("SRF"), + StoredAmount: []byte("1.00"), + TransferAmount: "1000000", + PublicKey: []byte("0X13242618721"), + Recipient: []byte("0x12415ass27192"), + ActiveDecimal: []byte("6"), + ActiveAddress: []byte("0xd4c288865Ce"), + TransferResponse: &models.TokenTransferResponse{ + TrackingId: "1234567890", + }, expectedResult: resource.Result{ FlagReset: []uint32{account_authorized_flag}, - Content: "Your request has been sent. 0x12415ass27192 will receive 0.002 SRF from 254712345678.", + Content: "Your request has been sent. 0711223344 will receive 1.00 SRF from 254712345678.", }, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(tt.Amount)) - if err != nil { - t.Fatal(err) - } - err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(tt.Recipient)) + err := store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(tt.TemporaryValue)) if err != nil { t.Fatal(err) } @@ -1259,9 +1268,31 @@ func TestInitiateTransaction(t *testing.T) { if err != nil { t.Fatal(err) } + err = store.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte(tt.StoredAmount)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(tt.PublicKey)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(tt.Recipient)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_DECIMAL, []byte(tt.ActiveDecimal)) + if err != nil { + t.Fatal(err) + } + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS, []byte(tt.ActiveAddress)) + if err != nil { + t.Fatal(err) + } + + mockAccountService.On("TokenTransfer").Return(tt.TransferResponse, nil) // Call the method under test - res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", tt.input) + res, _ := h.InitiateTransaction(ctx, "transaction_reset_amount", []byte("")) // Assert that no errors occurred assert.NoError(t, err) @@ -1453,10 +1484,12 @@ func TestValidateRecipient(t *testing.T) { } sessionId := "session123" + publicKey := "0X13242618721" ctx, store := InitializeTestStore(t) ctx = context.WithValue(ctx, "SessionId", sessionId) flag_invalid_recipient, _ := fm.parser.GetFlag("flag_invalid_recipient") + flag_invalid_recipient_with_invite, _ := fm.parser.GetFlag("flag_invalid_recipient_with_invite") // Define test cases tests := []struct { @@ -1466,19 +1499,33 @@ func TestValidateRecipient(t *testing.T) { }{ { name: "Test with invalid recepient", - input: []byte("000"), + input: []byte("9234adf5"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_recipient}, - Content: "000", + Content: "9234adf5", }, }, { - name: "Test with valid recepient", - input: []byte("0705X2"), + name: "Test with valid unregistered recepient", + input: []byte("0712345678"), + expectedResult: resource.Result{ + FlagSet: []uint32{flag_invalid_recipient_with_invite}, + Content: "0712345678", + }, + }, + { + name: "Test with valid registered recepient", + input: []byte("0711223344"), expectedResult: resource.Result{}, }, } + // store a public key for the valid recipient + err = store.WriteEntry(ctx, "0711223344", common.DATA_PUBLIC_KEY, []byte(publicKey)) + if err != nil { + t.Fatal(err) + } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create the Handlers instance @@ -1987,7 +2034,7 @@ func TestSetVoucher(t *testing.T) { t.Fatal(err) } - res, err := h.SetVoucher(ctx, "set_voucher", []byte{}) + res, err := h.SetVoucher(ctx, "set_voucher", []byte("")) assert.NoError(t, err) From d3fae34290049dc67d00f24575439b2f89b2fd4a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 13:24:54 +0300 Subject: [PATCH 144/332] modified the send node traversal --- menutraversal_test/test_setup.json | 32 ++++++------------------------ 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 3acb889..a554939 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -53,7 +53,7 @@ ] }, { - "name": "send_with_invalid_inputs", + "name": "send_with_invite", "steps": [ { "input": "", @@ -65,39 +65,19 @@ }, { "input": "000", - "expectedContent": "000 is not registered or invalid, please try again:\n1:Retry\n9:Quit" + "expectedContent": "000 is invalid, please try again:\n1:Retry\n9:Quit" }, { "input": "1", "expectedContent": "Enter recipient's phone number:\n0:Back" }, { - "input": "065656", - "expectedContent": "{max_amount}\nEnter amount:\n0:Back" + "input": "0712345678", + "expectedContent": "0712345678 is not registered, please try again:\n1:Retry\n2:Invite to Sarafu Network\n9:Quit" }, { - "input": "10000000", - "expectedContent": "Amount 10000000 is invalid, please try again:\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "{max_amount}\nEnter amount:\n0:Back" - }, - { - "input": "1.00", - "expectedContent": "065656 will receive {send_amount} from {session_id}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" - }, - { - "input": "1222", - "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" - }, - { - "input": "1", - "expectedContent": "065656 will receive {send_amount} from {session_id}\nPlease enter your PIN to confirm:\n0:Back\n9:Quit" - }, - { - "input": "1234", - "expectedContent": "Your request has been sent. 065656 will receive {send_amount} from {session_id}." + "input": "2", + "expectedContent": "Your invite request for 0712345678 to Sarafu Network failed. Please try again later." } ] }, From 51bf2534b87d1398202e8ec85410b1d21a95dd60 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 18:35:49 +0300 Subject: [PATCH 145/332] use a single bearer token --- .env.example | 3 +-- config/config.go | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index d392c9b..c636fa8 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,5 @@ DB_TIMEZONE=Africa/Nairobi #External API Calls CUSTODIAL_URL_BASE=http://localhost:5003 -CUSTODIAL_BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr +BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr DATA_URL_BASE=http://localhost:5006 -DATA_BEARER_TOKEN=eyJeSIsIRcCI6IXVCJ.yJwdWJsaLZXkiOiIwrrrrrr diff --git a/config/config.go b/config/config.go index 0d39e6e..edb33fe 100644 --- a/config/config.go +++ b/config/config.go @@ -18,10 +18,9 @@ const ( ) var ( - custodialURLBase string - dataURLBase string - CustodialBearerToken string - DataBearerToken string + custodialURLBase string + dataURLBase string + BearerToken string ) var ( @@ -40,8 +39,7 @@ func setBase() error { custodialURLBase = initializers.GetEnv("CUSTODIAL_URL_BASE", "http://localhost:5003") dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006") - CustodialBearerToken = initializers.GetEnv("CUSTODIAL_BEARER_TOKEN", "") - DataBearerToken = initializers.GetEnv("DATA_BEARER_TOKEN", "") + BearerToken = initializers.GetEnv("BEARER_TOKEN", "") _, err = url.JoinPath(custodialURLBase, "/foo") if err != nil { From baeb5e0ccba55d937a985e4b5313ba573bfe4850 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 18:37:20 +0300 Subject: [PATCH 146/332] use a single doRequest and updated the structs for TokenHoldings and Transfers --- remote/accountservice.go | 43 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 3af1ab1..6c94a28 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -51,7 +51,7 @@ func (as *AccountService) TrackAccountStatus(ctx context.Context, publicKey stri return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doRequest(ctx, req, &r) if err != nil { return nil, err } @@ -75,7 +75,7 @@ func (as *AccountService) CheckBalance(ctx context.Context, publicKey string) (* return nil, err } - _, err = doCustodialRequest(ctx, req, &balanceResult) + _, err = doRequest(ctx, req, &balanceResult) return &balanceResult, err } @@ -92,7 +92,7 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes if err != nil { return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doRequest(ctx, req, &r) if err != nil { return nil, err } @@ -104,7 +104,9 @@ func (as *AccountService) CreateAccount(ctx context.Context) (*models.AccountRes // Parameters: // - publicKey: The public key associated with the account. func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { - var r []dataserviceapi.TokenHoldings + var r struct { + Holdings []dataserviceapi.TokenHoldings `json:"holdings"` + } ep, err := url.JoinPath(config.VoucherHoldingsURL, publicKey) if err != nil { @@ -116,19 +118,21 @@ func (as *AccountService) FetchVouchers(ctx context.Context, publicKey string) ( return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doRequest(ctx, req, &r) if err != nil { return nil, err } - return r, nil + return r.Holdings, nil } // FetchTransactions retrieves the last 10 transactions for a given public key from the data indexer API endpoint // Parameters: // - publicKey: The public key associated with the account. func (as *AccountService) FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) { - var r []dataserviceapi.Last10TxResponse + var r struct { + Transfers []dataserviceapi.Last10TxResponse `json:"transfers"` + } ep, err := url.JoinPath(config.VoucherTransfersURL, publicKey) if err != nil { @@ -140,12 +144,12 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin return nil, err } - _, err = doDataRequest(ctx, req, r) + _, err = doRequest(ctx, req, &r) if err != nil { return nil, err } - return r, nil + return r.Transfers, nil } // VoucherData retrieves voucher metadata from the data indexer API endpoint. @@ -164,7 +168,7 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod return nil, err } - _, err = doCustodialRequest(ctx, req, &voucherDataResult) + _, err = doRequest(ctx, req, &voucherDataResult) return &voucherDataResult, err } @@ -195,7 +199,7 @@ func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, t if err != nil { return nil, err } - _, err = doCustodialRequest(ctx, req, &r) + _, err = doRequest(ctx, req, &r) if err != nil { return nil, err } @@ -206,7 +210,12 @@ func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, t func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse + + req.Header.Set("Authorization", "Bearer "+config.BearerToken) req.Header.Set("Content-Type", "application/json") + + logRequestDetails(req) + resp, err := http.DefaultClient.Do(req) if err != nil { log.Printf("Failed to make %s request to endpoint: %s with reason: %s", req.Method, req.URL, err.Error()) @@ -244,18 +253,6 @@ func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKRespons return &okResponse, err } -func doCustodialRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - req.Header.Set("Authorization", "Bearer "+config.CustodialBearerToken) - logRequestDetails(req) - return doRequest(ctx, req, rcpt) -} - -func doDataRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { - req.Header.Set("Authorization", "Bearer "+config.DataBearerToken) - logRequestDetails(req) - return doRequest(ctx, req, rcpt) -} - func logRequestDetails(req *http.Request) { var bodyBytes []byte contentType := req.Header.Get("Content-Type") From c8fc32a4e7a618c5a55ddb96536251cfa0265a8e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 18:50:06 +0300 Subject: [PATCH 147/332] cleaned up models --- models/accountresponse.go | 2 +- models/balanceresponse.go | 1 - models/tokenresponse.go | 18 ------------------ models/trackstatusresponse.go | 2 +- models/voucher_data_result.go | 8 ++++++++ models/vouchersresponse.go | 21 --------------------- 6 files changed, 10 insertions(+), 42 deletions(-) delete mode 100644 models/tokenresponse.go create mode 100644 models/voucher_data_result.go delete mode 100644 models/vouchersresponse.go diff --git a/models/accountresponse.go b/models/accountresponse.go index 278e0e9..dc8e758 100644 --- a/models/accountresponse.go +++ b/models/accountresponse.go @@ -1,6 +1,6 @@ package models type AccountResult struct { - PublicKey string `json:"publicKey"` + PublicKey string `json:"publicKey"` TrackingId string `json:"trackingId"` } diff --git a/models/balanceresponse.go b/models/balanceresponse.go index b2baa41..88e9ce9 100644 --- a/models/balanceresponse.go +++ b/models/balanceresponse.go @@ -2,7 +2,6 @@ package models import "encoding/json" - type BalanceResult struct { Balance string `json:"balance"` Nonce json.Number `json:"nonce"` diff --git a/models/tokenresponse.go b/models/tokenresponse.go deleted file mode 100644 index d243d93..0000000 --- a/models/tokenresponse.go +++ /dev/null @@ -1,18 +0,0 @@ -package models - -type ApiResponse struct { - OK bool `json:"ok"` - Description string `json:"description"` - Result Result `json:"result"` -} - -type Result struct { - Holdings []Holding `json:"holdings"` -} - -type Holding struct { - ContractAddress string `json:"contractAddress"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - Balance string `json:"balance"` -} diff --git a/models/trackstatusresponse.go b/models/trackstatusresponse.go index 47d757d..0c3c230 100644 --- a/models/trackstatusresponse.go +++ b/models/trackstatusresponse.go @@ -14,5 +14,5 @@ type Transaction struct { } type TrackStatusResult struct { - Active bool `json:"active"` + Active bool `json:"active"` } diff --git a/models/voucher_data_result.go b/models/voucher_data_result.go new file mode 100644 index 0000000..f04aafe --- /dev/null +++ b/models/voucher_data_result.go @@ -0,0 +1,8 @@ +package models + +type VoucherDataResult struct { + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals string `json:"tokenDecimals"` + SinkAddress string `json:"sinkAddress"` +} diff --git a/models/vouchersresponse.go b/models/vouchersresponse.go deleted file mode 100644 index 8cf3ec6..0000000 --- a/models/vouchersresponse.go +++ /dev/null @@ -1,21 +0,0 @@ -package models - -import dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" - -//type VoucherHoldingResponse struct { -// Ok bool `json:"ok"` -// Description string `json:"description"` -// Result VoucherResult `json:"result"` -//} - -// VoucherResult holds the list of token holdings -type VoucherResult struct { - Holdings []dataserviceapi.TokenHoldings `json:"holdings"` -} - -type VoucherDataResult struct { - TokenName string `json:"tokenName"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` - SinkAddress string `json:"sinkAddress"` -} From 7d16b710d81f31c78480a0a4cf101c1e42dfd8b4 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 20:50:07 +0300 Subject: [PATCH 148/332] set the TokenDecimals as int to match the API response --- models/voucher_data_result.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/voucher_data_result.go b/models/voucher_data_result.go index f04aafe..c9f0b74 100644 --- a/models/voucher_data_result.go +++ b/models/voucher_data_result.go @@ -3,6 +3,6 @@ package models type VoucherDataResult struct { TokenName string `json:"tokenName"` TokenSymbol string `json:"tokenSymbol"` - TokenDecimals string `json:"tokenDecimals"` + TokenDecimals int `json:"tokenDecimals"` SinkAddress string `json:"sinkAddress"` } From c0ccdce0a9e2d2a574a55b223b5ebab6d2008299 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 21:02:44 +0300 Subject: [PATCH 149/332] add the voucher details node --- services/registration/my_vouchers.vis | 1 + services/registration/voucher_details | 1 + services/registration/voucher_details.vis | 6 ++++++ services/registration/voucher_details_swa | 1 + 4 files changed, 9 insertions(+) create mode 100644 services/registration/voucher_details create mode 100644 services/registration/voucher_details.vis create mode 100644 services/registration/voucher_details_swa diff --git a/services/registration/my_vouchers.vis b/services/registration/my_vouchers.vis index b59441a..e79438e 100644 --- a/services/registration/my_vouchers.vis +++ b/services/registration/my_vouchers.vis @@ -6,3 +6,4 @@ MOUT back 0 HALT INCMP _ 0 INCMP select_voucher 1 +INCMP voucher_details 2 diff --git a/services/registration/voucher_details b/services/registration/voucher_details new file mode 100644 index 0000000..d437681 --- /dev/null +++ b/services/registration/voucher_details @@ -0,0 +1 @@ +{{.get_voucher_details}} \ No newline at end of file diff --git a/services/registration/voucher_details.vis b/services/registration/voucher_details.vis new file mode 100644 index 0000000..1b009f1 --- /dev/null +++ b/services/registration/voucher_details.vis @@ -0,0 +1,6 @@ +CATCH no_voucher flag_no_active_voucher 1 +LOAD get_voucher_details 0 +MAP get_voucher_details +MOUT back 0 +HALT +INCMP _ 0 diff --git a/services/registration/voucher_details_swa b/services/registration/voucher_details_swa new file mode 100644 index 0000000..d437681 --- /dev/null +++ b/services/registration/voucher_details_swa @@ -0,0 +1 @@ +{{.get_voucher_details}} \ No newline at end of file From 11bb194f264d22eeda652035f744a77bfad9e325 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 21:03:29 +0300 Subject: [PATCH 150/332] add a struct to access the tokenDetails --- remote/accountservice.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/remote/accountservice.go b/remote/accountservice.go index 6c94a28..23b62ca 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -156,7 +156,9 @@ func (as *AccountService) FetchTransactions(ctx context.Context, publicKey strin // Parameters: // - address: The voucher address. func (as *AccountService) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) { - var voucherDataResult models.VoucherDataResult + var r struct { + TokenDetails models.VoucherDataResult `json:"tokenDetails"` + } ep, err := url.JoinPath(config.VoucherDataURL, address) if err != nil { @@ -168,8 +170,8 @@ func (as *AccountService) VoucherData(ctx context.Context, address string) (*mod return nil, err } - _, err = doRequest(ctx, req, &voucherDataResult) - return &voucherDataResult, err + _, err = doRequest(ctx, req, &r) + return &r.TokenDetails, err } // TokenTransfer creates a new token transfer in the custodial system. From b40ad782949f682e02eb90f6347e6c122476a8f1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 15 Nov 2024 21:03:57 +0300 Subject: [PATCH 151/332] add the GetVoucherDetails function --- internal/handlers/handlerservice.go | 1 + internal/handlers/ussd/menuhandler.go | 33 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 358d492..e0cad8f 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -109,6 +109,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) ls.DbRs.AddLocalFunc("view_voucher", ussdHandlers.ViewVoucher) ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) + ls.DbRs.AddLocalFunc("get_voucher_details", ussdHandlers.GetVoucherDetails) ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin) ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 0b9360f..8a3e8cf 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1624,3 +1624,36 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re res.Content = tempData.TokenSymbol return res, nil } + +// GetVoucherDetails retrieves the voucher details +func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + store := h.userdataStore + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + + // get the active address + activeAddress, err := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read activeAddress entry with", "key", common.DATA_ACTIVE_ADDRESS, "error", err) + return res, err + } + + // use the voucher contract address to get the data from the API + voucherData, err := h.accountService.VoucherData(ctx, string(activeAddress)) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + return res, nil + } + + tokenSymbol := voucherData.TokenSymbol + tokenName := voucherData.TokenName + + res.Content = fmt.Sprintf("%s %s", tokenSymbol, tokenName) + + return res, nil +} From 5dd4f2a3fbba946c5d16d710a5dd8e17dd68e9a9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 16 Nov 2024 14:52:02 +0300 Subject: [PATCH 152/332] scale down the balance according to the decimals --- common/vouchers.go | 29 +++++++++++++++++++++++++-- internal/handlers/ussd/menuhandler.go | 8 ++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/common/vouchers.go b/common/vouchers.go index 54ace28..81ca55f 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -3,6 +3,7 @@ package common import ( "context" "fmt" + "math/big" "strings" "git.grassecon.net/urdt/ussd/internal/storage" @@ -24,7 +25,11 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { for i, h := range holdings { symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) - balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance)) + + // Scale down the balance + scaledBalance := ScaleDownBalance(h.Balance, h.TokenDecimals) + + balances = append(balances, fmt.Sprintf("%d:%s", i+1, scaledBalance)) decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals)) addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress)) } @@ -37,6 +42,26 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { return data } +func ScaleDownBalance(balance, decimals string) string { + // Convert balance and decimals to big.Float + bal := new(big.Float) + bal.SetString(balance) + + dec, ok := new(big.Int).SetString(decimals, 10) + if !ok { + dec = big.NewInt(0) // Default to 0 decimals in case of conversion failure + } + + divisor := new(big.Float).SetInt(new(big.Int).Exp(big.NewInt(10), dec, nil)) + scaledBalance := new(big.Float).Quo(bal, divisor) + + // Return the scaled balance without trailing decimals if it's an integer + if scaledBalance.IsInt() { + return scaledBalance.Text('f', 0) + } + return scaledBalance.Text('f', -1) +} + // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []string{"sym", "bal", "deci", "addr"} @@ -75,7 +100,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, decList := strings.Split(decimals, "\n") addrList := strings.Split(addresses, "\n") - logg.Tracef("found" , "symlist", symList, "syms", symbols, "input", input) + logg.Tracef("found", "symlist", symList, "syms", symbols, "input", input) for i, sym := range symList { parts := strings.SplitN(sym, ":", 2) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8a3e8cf..f2d3173 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1468,6 +1468,9 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by defaultDec := firstVoucher.TokenDecimals defaultAddr := firstVoucher.ContractAddress + // Scale down the balance + scaledBalance := common.ScaleDownBalance(defaultBal, defaultDec) + // set the active symbol err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_SYM, []byte(defaultSym)) if err != nil { @@ -1475,9 +1478,9 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by return res, err } // set the active balance - err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(defaultBal)) + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_BAL, []byte(scaledBalance)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", common.DATA_ACTIVE_BAL, "value", defaultBal, "error", err) + logg.ErrorCtxf(ctx, "failed to write defaultBal entry with", "key", common.DATA_ACTIVE_BAL, "value", scaledBalance, "error", err) return res, err } // set the active decimals @@ -1563,6 +1566,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) } // ViewVoucher retrieves the token holding and balance from the subprefixDB +// and displays it to the user for them to select it func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From 1ba90a8b780bf109c68aad4e31fbefb616787e95 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 16 Nov 2024 14:52:24 +0300 Subject: [PATCH 153/332] updated the test --- common/vouchers_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 8b9fa2a..9f5bf85 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -59,13 +59,13 @@ func TestMatchVoucher(t *testing.T) { func TestProcessVouchers(t *testing.T) { holdings := []dataserviceapi.TokenHoldings{ - {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100"}, - {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, + {ContractAddress: "0xd4c288865Ce", TokenSymbol: "SRF", TokenDecimals: "6", Balance: "100000000"}, + {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200000000"}, } expectedResult := VoucherMetadata{ Symbols: "1:SRF\n2:MILO", - Balances: "1:100\n2:200", + Balances: "1:100\n2:20000", Decimals: "1:6\n2:4", Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa", } From e1506a3dcf0ac6fa0e21494ea22b3e0648133754 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:20:12 +0300 Subject: [PATCH 154/332] show a placehlolder community balance --- internal/handlers/ussd/menuhandler.go | 42 +++++---------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f2d3173..d8e6fa0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -822,42 +822,14 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, nil } -func (h *Handlers) FetchCustodialBalances(ctx context.Context, sym string, input []byte) (resource.Result, error) { +func (h *Handlers) FetchCommunityBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - - flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") - - sessionId, ok := ctx.Value("SessionId").(string) - if !ok { - return res, fmt.Errorf("missing session") - } - symbol, _ := h.st.Where() - balanceType := strings.Split(symbol, "_")[0] - - store := h.userdataStore - publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) - if err != nil { - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) - return res, err - } - - balanceResponse, err := h.accountService.CheckBalance(ctx, string(publicKey)) - if err != nil { - res.FlagSet = append(res.FlagSet, flag_api_error) - return res, nil - } - res.FlagReset = append(res.FlagReset, flag_api_error) - - balance := balanceResponse.Balance - - switch balanceType { - case "my": - res.Content = fmt.Sprintf("Your balance is %s", balance) - case "community": - res.Content = fmt.Sprintf("Your community balance is %s", balance) - default: - break - } + code := codeFromCtx(ctx) + l := gotext.NewLocale(translationDir, code) + l.AddDomain("default") + //TODO: + //Check if the address is a community account,if then,get the actual balance + res.Content = l.Get("Community Balance: 0.00") return res, nil } From 454f67b3177bce34cbd6448b2ef3263c645eec76 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:21:04 +0300 Subject: [PATCH 155/332] show balance based on current voucher --- services/registration/my_balance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/my_balance b/services/registration/my_balance index f8f8318..afae8c1 100644 --- a/services/registration/my_balance +++ b/services/registration/my_balance @@ -1 +1 @@ -{{.fetch_custodial_balances}} \ No newline at end of file +{{.check_balance}} \ No newline at end of file From 44c52b6ed717f619d0912fd60f90653fed4a6782 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:23:36 +0300 Subject: [PATCH 156/332] rename fetch_custodial_balances -> fetch_community_balance --- internal/handlers/handlerservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index e0cad8f..fe88c1f 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -103,7 +103,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin) ls.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange) ls.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp) - ls.DbRs.AddLocalFunc("fetch_custodial_balances", ussdHandlers.FetchCustodialBalances) + ls.DbRs.AddLocalFunc("fetch_community_balance", ussdHandlers.FetchCommunityBalance) ls.DbRs.AddLocalFunc("set_default_voucher", ussdHandlers.SetDefaultVoucher) ls.DbRs.AddLocalFunc("check_vouchers", ussdHandlers.CheckVouchers) ls.DbRs.AddLocalFunc("get_vouchers", ussdHandlers.GetVoucherList) From 1d4f11607963437e46fa9145c22f17340193e679 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:23:59 +0300 Subject: [PATCH 157/332] community balance str --- services/registration/locale/swa/default.po | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index f933dde..aa033b5 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -21,3 +21,6 @@ msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu limetumwa." msgid "Your request failed. Please try again later." msgstr "Ombi lako halikufaulu. Tafadhali jaribu tena baadaye." + +msgid "Community Balance: 0.00" +msgid "Salio la Kikundi: 0.00" From f2b17880bab2f45d4a4b9b03b8f25e1905c933ee Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:30:09 +0300 Subject: [PATCH 158/332] check community and my balance separately --- services/registration/comminity_balance_swa | 2 +- services/registration/community_balance | 2 +- services/registration/community_balance.vis | 4 ++-- services/registration/my_balance.vis | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/registration/comminity_balance_swa b/services/registration/comminity_balance_swa index 726fc99..d9f1d6e 100644 --- a/services/registration/comminity_balance_swa +++ b/services/registration/comminity_balance_swa @@ -1 +1 @@ -Salio la kikundi \ No newline at end of file +{{.fetch_community_balance}} \ No newline at end of file diff --git a/services/registration/community_balance b/services/registration/community_balance index f8f8318..d9f1d6e 100644 --- a/services/registration/community_balance +++ b/services/registration/community_balance @@ -1 +1 @@ -{{.fetch_custodial_balances}} \ No newline at end of file +{{.fetch_community_balance}} \ No newline at end of file diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index 85ae93a..f3e0ae1 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,7 +1,7 @@ LOAD reset_incorrect 6 -LOAD fetch_custodial_balances 0 +LOAD fetch_community_balance 0 CATCH api_failure flag_api_call_error 1 -MAP fetch_custodial_balances +MAP fetch_community_balance CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 MOUT back 0 diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index 85ae93a..9144da9 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,7 +1,7 @@ LOAD reset_incorrect 6 -LOAD fetch_custodial_balances 0 +LOAD check_balance 0 CATCH api_failure flag_api_call_error 1 -MAP fetch_custodial_balances +MAP check_balance CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 MOUT back 0 From 01e75e92170cb26b81dbadd31fd2057f105972e6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 18 Nov 2024 17:30:34 +0300 Subject: [PATCH 159/332] update test --- internal/handlers/ussd/menuhandler_test.go | 42 +++++++--------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index ff6f0ad..51693df 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2,7 +2,6 @@ package ussd import ( "context" - "encoding/json" "fmt" "log" "path" @@ -1773,58 +1772,43 @@ func TestConfirmPin(t *testing.T) { } } -func TestFetchCustodialBalances(t *testing.T) { - fm, err := NewFlagManager(flagsPath) - if err != nil { - t.Logf(err.Error()) - } - flag_api_error, _ := fm.GetFlag("flag_api_call_error") +func TestFetchCommunityBalance(t *testing.T) { // Define test data sessionId := "session123" - publicKey := "0X13242618721" - ctx, store := InitializeTestStore(t) - ctx = context.WithValue(ctx, "SessionId", sessionId) - - err = store.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(publicKey)) - if err != nil { - t.Fatal(err) - } tests := []struct { - name string - balanceResponse *models.BalanceResult - expectedResult resource.Result + name string + languageCode string + expectedResult resource.Result }{ { - name: "Test when fetch custodial balances is not a success", - balanceResponse: &models.BalanceResult{ - Balance: "0.003 CELO", - Nonce: json.Number("0"), - }, + name: "Test community balance content when language is english", expectedResult: resource.Result{ - FlagReset: []uint32{flag_api_error}, + Content: "Community Balance: 0.00", }, + languageCode: "eng", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAccountService := new(mocks.MockAccountService) mockState := state.NewState(16) h := &Handlers{ userdataStore: store, - flagManager: fm.parser, st: mockState, accountService: mockAccountService, } - - // Set up the expected behavior of the mock - mockAccountService.On("CheckBalance", string(publicKey)).Return(tt.balanceResponse, nil) + ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Language", lang.Language{ + Code: tt.languageCode, + }) // Call the method - res, _ := h.FetchCustodialBalances(ctx, "fetch_custodial_balances", []byte("")) + res, _ := h.FetchCommunityBalance(ctx, "fetch_community_balance", []byte("")) //Assert that the result set to content is what was expected assert.Equal(t, res, tt.expectedResult, "Result should match expected result") From bc6d8098f3cc25b69d920b62663f1b72bdf251f8 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 10:15:34 +0300 Subject: [PATCH 160/332] updated the failed test --- menutraversal_test/group_test.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 449c42f..52fab59 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -103,7 +103,7 @@ }, { "input": "1234", - "expectedContent": "Your balance is 0.003 CELO\n0:Back\n9:Quit" + "expectedContent": "Balance: {balance}\n\n0:Back\n9:Quit" }, { "input": "0", @@ -149,7 +149,7 @@ }, { "input": "1234", - "expectedContent": "Your community balance is 0.003 CELO\n0:Back\n9:Quit" + "expectedContent": "{balance}\n0:Back\n9:Quit" }, { "input": "0", From 1d9f4fc13e2e44c3b2e058e255da1e433cd56b17 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 15:50:19 +0300 Subject: [PATCH 161/332] added statement related flags --- services/registration/pp.csv | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 406cc22..cd6a633 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -19,3 +19,5 @@ flag,flag_api_call_error,25,this is set when communication to an external servic flag,flag_no_active_voucher,26,this is set when a user does not have an active voucher flag,flag_admin_privilege,27,this is set when a user has admin privileges. flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action +flag,flag_no_transfers,29,this is set when a user does not have any transactions +flag,flag_incorrect_statement,30,this is set when the selected statement is invalid From d49b68597c6b928a78e38e2bca79af9e6ffc42cd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:03:54 +0300 Subject: [PATCH 162/332] check transactions and catch when none exist --- services/registration/check_statement.vis | 3 +++ services/registration/no_transfers | 1 + services/registration/no_transfers.vis | 5 +++++ services/registration/no_transfers_swa | 1 + 4 files changed, 10 insertions(+) create mode 100644 services/registration/no_transfers create mode 100644 services/registration/no_transfers.vis create mode 100644 services/registration/no_transfers_swa diff --git a/services/registration/check_statement.vis b/services/registration/check_statement.vis index 596dcab..d79b5ca 100644 --- a/services/registration/check_statement.vis +++ b/services/registration/check_statement.vis @@ -1,3 +1,6 @@ +LOAD check_transactions 0 +RELOAD check_transactions +CATCH no_transfers flag_no_transfers 1 LOAD authorize_account 6 MOUT back 0 MOUT quit 9 diff --git a/services/registration/no_transfers b/services/registration/no_transfers new file mode 100644 index 0000000..3439806 --- /dev/null +++ b/services/registration/no_transfers @@ -0,0 +1 @@ +No transfers history \ No newline at end of file diff --git a/services/registration/no_transfers.vis b/services/registration/no_transfers.vis new file mode 100644 index 0000000..832ef22 --- /dev/null +++ b/services/registration/no_transfers.vis @@ -0,0 +1,5 @@ +MOUT back 0 +MOUT quit 9 +HALT +INCMP ^ 0 +INCMP quit 9 diff --git a/services/registration/no_transfers_swa b/services/registration/no_transfers_swa new file mode 100644 index 0000000..1f82e82 --- /dev/null +++ b/services/registration/no_transfers_swa @@ -0,0 +1 @@ +Hakuna historia kwa akaunti yako \ No newline at end of file From bfcdd79f336c6fb51c5ae3163ee978952d21ada2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:04:46 +0300 Subject: [PATCH 163/332] view single statement --- services/registration/transactions.vis | 4 ++++ services/registration/view_statement | 1 + services/registration/view_statement.vis | 10 ++++++++++ 3 files changed, 15 insertions(+) create mode 100644 services/registration/view_statement create mode 100644 services/registration/view_statement.vis diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis index 2317ff0..33ccc89 100644 --- a/services/registration/transactions.vis +++ b/services/registration/transactions.vis @@ -5,7 +5,11 @@ MOUT quit 99 MNEXT next 11 MPREV prev 22 HALT +LOAD view_statement 0 +RELOAD view_statement +CATCH . flag_incorrect_statement 1 INCMP _ 0 INCMP quit 99 INCMP > 11 INCMP < 22 +INCMP view_statement * diff --git a/services/registration/view_statement b/services/registration/view_statement new file mode 100644 index 0000000..1285cf9 --- /dev/null +++ b/services/registration/view_statement @@ -0,0 +1 @@ +{{.view_statement}} \ No newline at end of file diff --git a/services/registration/view_statement.vis b/services/registration/view_statement.vis new file mode 100644 index 0000000..4dde523 --- /dev/null +++ b/services/registration/view_statement.vis @@ -0,0 +1,10 @@ +MAP view_statement +MOUT back 0 +MOUT quit 9 +MNEXT next 11 +MPREV prev 22 +HALT +INCMP _ 0 +INCMP quit 9 +INCMP > 11 +INCMP < 22 From 1d82bc2261e6a1540e74aaf293fcf34d3bf68d0a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:07:28 +0300 Subject: [PATCH 164/332] added transactions functions --- common/transfer_statements.go | 115 ++++++++++++++++++ internal/handlers/handlerservice.go | 4 +- internal/handlers/ussd/menuhandler.go | 166 +++++++++++++++++++++++++- 3 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 common/transfer_statements.go diff --git a/common/transfer_statements.go b/common/transfer_statements.go new file mode 100644 index 0000000..cc75e78 --- /dev/null +++ b/common/transfer_statements.go @@ -0,0 +1,115 @@ +package common + +import ( + "context" + "fmt" + "strings" + "time" + + "git.grassecon.net/urdt/ussd/internal/storage" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" +) + +// TransferMetadata helps organize data fields +type TransferMetadata struct { + Senders string + Recipients string + TransferValues string + Addresses string + TxHashes string + Dates string + Symbols string + Decimals string +} + +// ProcessTransfers converts transfers into formatted strings +func ProcessTransfers(transfers []dataserviceapi.Last10TxResponse) TransferMetadata { + var data TransferMetadata + var senders, recipients, transferValues, addresses, txHashes, dates, symbols, decimals []string + + for _, t := range transfers { + senders = append(senders, t.Sender) + recipients = append(recipients, t.Recipient) + + // Scale down the amount + scaledBalance := ScaleDownBalance(t.TransferValue, t.TokenDecimals) + transferValues = append(transferValues, scaledBalance) + + addresses = append(addresses, t.ContractAddress) + txHashes = append(txHashes, t.TxHash) + dates = append(dates, fmt.Sprintf("%s", t.DateBlock)) + symbols = append(symbols, t.TokenSymbol) + decimals = append(decimals, t.TokenDecimals) + } + + data.Senders = strings.Join(senders, "\n") + data.Recipients = strings.Join(recipients, "\n") + data.TransferValues = strings.Join(transferValues, "\n") + data.Addresses = strings.Join(addresses, "\n") + data.TxHashes = strings.Join(txHashes, "\n") + data.Dates = strings.Join(dates, "\n") + data.Symbols = strings.Join(symbols, "\n") + data.Decimals = strings.Join(decimals, "\n") + + return data +} + +// GetTransferData retrieves and matches transfer data +// returns a formatted string of the full transaction/statement +func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, index int) (string, error) { + keys := []string{"txfrom", "txto", "txval", "txaddr", "txhash", "txdate", "txsym"} + data := make(map[string]string) + + for _, key := range keys { + value, err := db.Get(ctx, []byte(key)) + if err != nil { + return "", fmt.Errorf("failed to get %s: %v", key, err) + } + data[key] = string(value) + } + + // Split the data + senders := strings.Split(string(data["txfrom"]), "\n") + recipients := strings.Split(string(data["txto"]), "\n") + values := strings.Split(string(data["txval"]), "\n") + addresses := strings.Split(string(data["txaddr"]), "\n") + hashes := strings.Split(string(data["txhash"]), "\n") + dates := strings.Split(string(data["txdate"]), "\n") + syms := strings.Split(string(data["txsym"]), "\n") + + // Adjust for 0-based indexing + i := index - 1 + transactionType := "received" + party := fmt.Sprintf("from: %s", strings.TrimSpace(senders[i])) + if strings.TrimSpace(senders[i]) == publicKey { + transactionType = "sent" + party = fmt.Sprintf("to: %s", strings.TrimSpace(recipients[i])) + } + + formattedDate := formatDate(strings.TrimSpace(dates[i])) + + // Build the full transaction detail + detail := fmt.Sprintf( + "%s %s %s\n%s\ncontract address: %s\ntxhash: %s\ndate: %s", + transactionType, + strings.TrimSpace(values[i]), + strings.TrimSpace(syms[i]), + party, + strings.TrimSpace(addresses[i]), + strings.TrimSpace(hashes[i]), + formattedDate, + ) + + return detail, nil +} + +// Helper function to format date in desired output +func formatDate(dateStr string) string { + fmt.Println(dateStr) + parsedDate, err := time.Parse("2006-01-02 15:04:05 -0700 MST", dateStr) + if err != nil { + fmt.Println("Error parsing date:", err) + return "" + } + return parsedDate.Format("2006-01-02 03:04:05 PM") +} diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 6d21d88..c77e82c 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -118,7 +118,9 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("reset_others_pin", ussdHandlers.ResetOthersPin) ls.DbRs.AddLocalFunc("save_others_temporary_pin", ussdHandlers.SaveOthersTemporaryPin) ls.DbRs.AddLocalFunc("get_current_profile_info", ussdHandlers.GetCurrentProfileInfo) - ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactions) + ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions) + ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList) + ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) return ussdHandlers, nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8b1ceb4..5896b90 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1634,11 +1634,171 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by return res, nil } -// GetTransactions retrieves the transactions from the API using the "PublicKey" -func (h *Handlers) GetTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { +// CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb +func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } - res.Content = "Transaction list" + flag_no_transfers, _ := h.flagManager.GetFlag("flag_no_transfers") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_error") + + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + // Fetch transactions from the API using the public key + transactionsResp, err := h.accountService.FetchTransactions(ctx, string(publicKey)) + if err != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + logg.ErrorCtxf(ctx, "failed on FetchTransactions", "error", err) + return res, err + } + + // Return if there are no transactions + if len(transactionsResp) == 0 { + res.FlagSet = append(res.FlagSet, flag_no_transfers) + return res, nil + } + + data := common.ProcessTransfers(transactionsResp) + + // Store all transaction data + dataMap := map[string]string{ + "txfrom": data.Senders, + "txto": data.Recipients, + "txval": data.TransferValues, + "txaddr": data.Addresses, + "txhash": data.TxHashes, + "txdate": data.Dates, + "txsym": data.Symbols, + "txdeci": data.Decimals, + } + + for key, value := range dataMap { + if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil { + logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) + return res, err + } + } + + res.FlagReset = append(res.FlagReset, flag_no_transfers) + + return res, nil +} + +// GetTransactionsList fetches the list of transactions and formats them +func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + // Read transactions from the store and format them + TransactionSenders, err := h.prefixDb.Get(ctx, []byte("txfrom")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) + return res, err + } + TransactionSyms, err := h.prefixDb.Get(ctx, []byte("txsym")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) + return res, err + } + TransactionValues, err := h.prefixDb.Get(ctx, []byte("txval")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) + return res, err + } + TransactionDates, err := h.prefixDb.Get(ctx, []byte("txdate")) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) + return res, err + } + + // Parse the data + senders := strings.Split(string(TransactionSenders), "\n") + syms := strings.Split(string(TransactionSyms), "\n") + values := strings.Split(string(TransactionValues), "\n") + dates := strings.Split(string(TransactionDates), "\n") + + var formattedTransactions []string + for i := 0; i < len(senders); i++ { + sender := strings.TrimSpace(senders[i]) + sym := strings.TrimSpace(syms[i]) + value := strings.TrimSpace(values[i]) + date := strings.Split(strings.TrimSpace(dates[i]), " ")[0] + + status := "received" + if sender == string(publicKey) { + status = "sent" + } + + formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d:%s %s %s %s", i+1, status, value, sym, date)) + } + + res.Content = strings.Join(formattedTransactions, "\n") + + return res, nil +} + +// ViewTransactionStatement retrieves the token holding and balance from the subprefixDB +// and displays it to the user for them to select it +func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + store := h.userdataStore + publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) + if err != nil { + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + flag_incorrect_statement, _ := h.flagManager.GetFlag("flag_incorrect_statement") + + inputStr := string(input) + if inputStr == "0" || inputStr == "99" || inputStr == "11" || inputStr == "22" { + res.FlagReset = append(res.FlagReset, flag_incorrect_statement) + return res, nil + } + + // Convert input string to integer + index, err := strconv.Atoi(strings.TrimSpace(inputStr)) + if err != nil { + return res, fmt.Errorf("invalid input: must be a number between 1 and 10") + } + + if index < 1 || index > 10 { + return res, fmt.Errorf("invalid input: index must be between 1 and 10") + } + + statement, err := common.GetTransferData(ctx, h.prefixDb, string(publicKey), index) + if err != nil { + return res, fmt.Errorf("failed to retrieve transfer data: %v", err) + } + + if statement == "" { + res.FlagSet = append(res.FlagSet, flag_incorrect_statement) + return res, nil + } + + res.FlagReset = append(res.FlagReset, flag_incorrect_statement) + res.Content = statement return res, nil } From 6159686a8e64131393fac364f6b11ccfa160909b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:21:55 +0300 Subject: [PATCH 165/332] update function comment --- internal/handlers/ussd/menuhandler.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5896b90..065da85 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1754,8 +1754,8 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] return res, nil } -// ViewTransactionStatement retrieves the token holding and balance from the subprefixDB -// and displays it to the user for them to select it +// ViewTransactionStatement retrieves the transaction statement +// and displays it to the user func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From eab6dbd74c1a7bb961b1ba2aadbc782831e19481 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:23:35 +0300 Subject: [PATCH 166/332] Check if index is within range --- common/transfer_statements.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index cc75e78..eec7e11 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -77,6 +77,11 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, dates := strings.Split(string(data["txdate"]), "\n") syms := strings.Split(string(data["txsym"]), "\n") + // Check if index is within range + if index < 1 || index > len(senders) { + return "", fmt.Errorf("transaction not found: index %d out of range", index) + } + // Adjust for 0-based indexing i := index - 1 transactionType := "received" From 9498242901804bd918a783612fbae9490a8724cd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 16:27:19 +0300 Subject: [PATCH 167/332] removed debug statement --- common/transfer_statements.go | 1 - 1 file changed, 1 deletion(-) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index eec7e11..4dd409c 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -110,7 +110,6 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, // Helper function to format date in desired output func formatDate(dateStr string) string { - fmt.Println(dateStr) parsedDate, err := time.Parse("2006-01-02 15:04:05 -0700 MST", dateStr) if err != nil { fmt.Println("Error parsing date:", err) From df89fe69e18096946fe977b3aed95a0d6b0e042d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 19 Nov 2024 17:09:59 +0300 Subject: [PATCH 168/332] return to the main node --- services/registration/transactions.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/transactions.vis b/services/registration/transactions.vis index 33ccc89..b21f1dc 100644 --- a/services/registration/transactions.vis +++ b/services/registration/transactions.vis @@ -8,7 +8,7 @@ HALT LOAD view_statement 0 RELOAD view_statement CATCH . flag_incorrect_statement 1 -INCMP _ 0 +INCMP ^ 0 INCMP quit 99 INCMP > 11 INCMP < 22 From 66b5843b0d8210959abb4e4f146897f0b14b2ba3 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 19 Nov 2024 15:15:24 +0100 Subject: [PATCH 169/332] feat: dockerfile and github based CI build for Africastalking variant (#174) ## Summary * fixed missing error handler in main * add injectable build string in main * add (dynamically linked) docker build * add github CI workflow * add extra but useful dev files in dev folder * closes #93 ## Notes * We'll move to a self-hosted CI runner once it is installed on Gitea. Reviewed-on: https://git.grassecon.net/urdt/ussd/pulls/174 Co-authored-by: Mohammed Sohail Co-committed-by: Mohammed Sohail --- .dockerignore | 13 ++++++++ .github/workflows/docker.yaml | 58 ++++++++++++++++++++++++++++++++++ Dockerfile | 40 +++++++++++++++++++++++ cmd/africastalking/main.go | 12 +++++-- dev/dialoguss/sample_user.yaml | 3 ++ dev/docker-compose.yaml | 21 ++++++++++++ dev/init_db.sql | 1 + 7 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yaml create mode 100644 Dockerfile create mode 100644 dev/dialoguss/sample_user.yaml create mode 100644 dev/docker-compose.yaml create mode 100644 dev/init_db.sql diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..668522b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +/** +!/cmd/africastalking +!/common +!/config +!/initializers +!/internal +!/models +!/remote +!/services +!/LICENSE +!/README.md +!/go.* +!/.env \ No newline at end of file diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000..77f4f78 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,58 @@ +name: release + +on: + push: + tags: + - "v*" + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Check out repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-buildx- + + - name: Login to GHCR Docker registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set outputs + run: | + echo "RELEASE_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV \ + && echo "RELEASE_SHORT_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV + + - name: Build and push image + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./Dockerfile + platforms: + - linux/amd64 + - linux/arm64 + push: true + build-args: | + BUILD=${{ env.RELEASE_SHORT_COMMIT }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + tags: | + ghcr.io/grassrootseconomics/urdt-ussd:latest + ghcr.io/grassrootseconomics/urdt-ussd:${{ env.RELEASE_TAG }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6d88926 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +FROM golang:1.23.0-bookworm AS build + +ENV CGO_ENABLED=1 + +ARG BUILDPLATFORM +ARG TARGETPLATFORM +ARG BUILD=dev + +WORKDIR /build +COPY . . +RUN apt update && apt install libgdbm-dev +RUN git clone https://git.defalsify.org/vise.git go-vise + +WORKDIR /build/services/registration +RUN echo "Compiling go-vise files" +RUN make VISE_PATH=/build/go-vise -B + +WORKDIR /build +RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM" +RUN go mod download +RUN go build -o ussd-africastalking -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go + +FROM debian:bookworm-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt update && apt install libgdbm-dev ca-certificates -y +RUN apt-get clean && rm -rf /var/lib/apt/lists/* + +WORKDIR /service + +COPY --from=build /build/ussd-africastalking . +COPY --from=build /build/LICENSE . +COPY --from=build /build/README.md . +COPY --from=build /build/services ./services +COPY --from=build /build/.env . + +EXPOSE 7123 + +CMD ["./ussd-africastalking"] \ No newline at end of file diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 5a523e7..9f5b501 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -29,8 +29,10 @@ import ( ) var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") + + build = "dev" ) func init() { @@ -115,7 +117,7 @@ func main() { flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") flag.Parse() - logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) + logg.Infof("start command", "build", build, "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) @@ -157,6 +159,10 @@ func main() { } lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } lhs.SetDataStore(&userdataStore) if err != nil { diff --git a/dev/dialoguss/sample_user.yaml b/dev/dialoguss/sample_user.yaml new file mode 100644 index 0000000..83e690e --- /dev/null +++ b/dev/dialoguss/sample_user.yaml @@ -0,0 +1,3 @@ +url: http://localhost:7123 +dial: "*384*96#" +phoneNumber: +254722123456 diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml new file mode 100644 index 0000000..5d65b54 --- /dev/null +++ b/dev/docker-compose.yaml @@ -0,0 +1,21 @@ +services: + ussd-pg-store: + image: postgres:17-alpine + restart: unless-stopped + user: postgres + environment: + - POSTGRES_PASSWORD=postgres + - POSTGRES_USER=postgres + volumes: + - ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql + - ussd-pg:/var/lib/postgresql/data + ports: + - "127.0.0.1:5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 +volumes: + ussd-pg: + driver: local diff --git a/dev/init_db.sql b/dev/init_db.sql new file mode 100644 index 0000000..9ee26b8 --- /dev/null +++ b/dev/init_db.sql @@ -0,0 +1 @@ +CREATE DATABASE urdt_ussd; \ No newline at end of file From 0a19a6c48b5037e5a91f887e8c43b789b6ed6d7a Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 19 Nov 2024 17:18:13 +0300 Subject: [PATCH 170/332] fix: github ci spec --- .github/workflows/docker.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 77f4f78..da35100 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -45,9 +45,7 @@ jobs: with: context: ./ file: ./Dockerfile - platforms: - - linux/amd64 - - linux/arm64 + platforms: linux/amd64 push: true build-args: | BUILD=${{ env.RELEASE_SHORT_COMMIT }} From 7adc0c9c084ee7ed83f2b95c999bde22ffd01d88 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 19 Nov 2024 17:25:20 +0300 Subject: [PATCH 171/332] fix: Dockerfile to include .env --- .dockerignore | 2 +- Dockerfile | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 668522b..a118f64 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,4 +10,4 @@ !/LICENSE !/README.md !/go.* -!/.env \ No newline at end of file +!/.env.example \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 6d88926..64ff175 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,8 @@ COPY --from=build /build/ussd-africastalking . COPY --from=build /build/LICENSE . COPY --from=build /build/README.md . COPY --from=build /build/services ./services -COPY --from=build /build/.env . +COPY --from=build /build/.env.example . +RUN mv .env.example .env EXPOSE 7123 From 212cd482498ff25b7a652eaf9f1346f2808fcbd3 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Tue, 19 Nov 2024 17:53:06 +0300 Subject: [PATCH 172/332] debug: postgres conn string --- Dockerfile | 2 +- internal/storage/storageservice.go | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 64ff175..3a5da7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ RUN make VISE_PATH=/build/go-vise -B WORKDIR /build RUN echo "Building on $BUILDPLATFORM, building for $TARGETPLATFORM" RUN go mod download -RUN go build -o ussd-africastalking -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go +RUN go build -tags logtrace -o ussd-africastalking -ldflags="-X main.build=${BUILD} -s -w" cmd/africastalking/main.go FROM debian:bookworm-slim diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index 9fa1839..ca28bbb 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -41,10 +41,13 @@ func buildConnStr() string { dbName := initializers.GetEnv("DB_NAME", "") port := initializers.GetEnv("DB_PORT", "5432") - return fmt.Sprintf( + connString := fmt.Sprintf( "postgres://%s:%s@%s:%s/%s", user, password, host, port, dbName, ) + logg.Debugf("pg conn string", "conn", connString) + + return connString } func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService { From 9cf1cbe4250f93ed77eacd58558101a3d21c70fa Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 20 Nov 2024 16:36:18 +0300 Subject: [PATCH 173/332] add swahili translation for catch node --- services/registration/_catch_swa | 1 + 1 file changed, 1 insertion(+) create mode 100644 services/registration/_catch_swa diff --git a/services/registration/_catch_swa b/services/registration/_catch_swa new file mode 100644 index 0000000..3affebd --- /dev/null +++ b/services/registration/_catch_swa @@ -0,0 +1 @@ +Tatizo la kimtambo limetokea,tafadhali jaribu tena baadaye. \ No newline at end of file From d20700ca7411cc9a9eaf88f40d92c56ec8d3551c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 20 Nov 2024 16:39:50 +0300 Subject: [PATCH 174/332] fix size limit error --- services/registration/main.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/main.vis b/services/registration/main.vis index 7e1c9bf..7a27808 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,6 +1,6 @@ LOAD set_default_voucher 8 RELOAD set_default_voucher -LOAD check_balance 64 +LOAD check_balance 128 RELOAD check_balance LOAD check_vouchers 10 RELOAD check_vouchers From 451b15fb6b19fd27cc754f8509f2ef72ace38f9c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 20 Nov 2024 17:13:14 +0300 Subject: [PATCH 175/332] explicit set_language reload --- services/registration/set_eng.vis | 1 + services/registration/set_swa.vis | 1 + 2 files changed, 2 insertions(+) diff --git a/services/registration/set_eng.vis b/services/registration/set_eng.vis index 662fd2d..b66a1b7 100644 --- a/services/registration/set_eng.vis +++ b/services/registration/set_eng.vis @@ -1,3 +1,4 @@ LOAD set_language 6 +RELOAD set_language CATCH terms flag_account_created 0 MOVE language_changed diff --git a/services/registration/set_swa.vis b/services/registration/set_swa.vis index 662fd2d..b66a1b7 100644 --- a/services/registration/set_swa.vis +++ b/services/registration/set_swa.vis @@ -1,3 +1,4 @@ LOAD set_language 6 +RELOAD set_language CATCH terms flag_account_created 0 MOVE language_changed From 7880294c6f9589bcad2da618945e786e7d42c35a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 20 Nov 2024 17:14:25 +0300 Subject: [PATCH 176/332] set eng as default language --- internal/handlers/ussd/menuhandler.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d8e6fa0..a58e344 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -154,7 +154,8 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r code := strings.Split(symbol, "_")[1] if !utils.IsValidISO639(code) { - return res, nil + //Fallback to english instead? + code = "eng" } res.FlagSet = append(res.FlagSet, state.FLAG_LANG) res.Content = code From 9b89462797be98f0477f1d7ea316a092cdd4791b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 21 Nov 2024 11:21:16 +0300 Subject: [PATCH 177/332] add function to check validity of provided yob --- internal/utils/age.go | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/internal/utils/age.go b/internal/utils/age.go index 6b040e7..59056a8 100644 --- a/internal/utils/age.go +++ b/internal/utils/age.go @@ -1,6 +1,9 @@ package utils -import "time" +import ( + "strconv" + "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. @@ -25,11 +28,29 @@ func CalculateAge(birthdate, today time.Time) int { // It subtracts the YOB from the current year to determine the age. // // Parameters: -// yob: The year of birth as an integer. +// +// yob: The year of birth as an integer. // // Returns: -// The calculated age as an integer. +// +// The calculated age as an integer. func CalculateAgeWithYOB(yob int) int { - currentYear := time.Now().Year() - return currentYear - yob -} \ No newline at end of file + currentYear := time.Now().Year() + return currentYear - yob +} + + +//IsValidYob checks if the provided yob can be considered valid +func IsValidYOb(yob string) bool { + currentYear := time.Now().Year() + yearOfBirth, err := strconv.ParseInt(yob, 10, 64) + if err != nil { + return false + } + if yearOfBirth >= 1900 && int(yearOfBirth) <= currentYear { + return true + } else { + return false + } + +} From 8925e26c4c85aec07702322925cefb608609228d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 21 Nov 2024 11:25:47 +0300 Subject: [PATCH 178/332] refactor check for valid yob --- internal/handlers/ussd/menuhandler.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a58e344..eeca3fd 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -154,7 +154,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r code := strings.Split(symbol, "_")[1] if !utils.IsValidISO639(code) { - //Fallback to english instead? + //Fallback to english instead? code = "eng" } res.FlagSet = append(res.FlagSet, state.FLAG_LANG) @@ -764,12 +764,11 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res return res, nil } - if len(date) == 4 { + if utils.IsValidYOb(date) { res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) } else { res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) } - return res, nil } From d1e9340ea9888acd1bbc7dbb0dc4f9e71b6c6004 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 21 Nov 2024 13:03:43 +0300 Subject: [PATCH 179/332] add voucher details --- models/voucher_data_result.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/models/voucher_data_result.go b/models/voucher_data_result.go index c9f0b74..9a10831 100644 --- a/models/voucher_data_result.go +++ b/models/voucher_data_result.go @@ -1,8 +1,10 @@ package models type VoucherDataResult struct { - TokenName string `json:"tokenName"` - TokenSymbol string `json:"tokenSymbol"` - TokenDecimals int `json:"tokenDecimals"` - SinkAddress string `json:"sinkAddress"` + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimals int `json:"tokenDecimals"` + SinkAddress string `json:"sinkAddress"` + TokenCommodity string `json:"tokenCommodity"` + TokenLocation string `json:"tokenLocation"` } From b8d938d3aa99ddecd999a2a68cf6b326b8f9b71f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 21 Nov 2024 13:04:19 +0300 Subject: [PATCH 180/332] add voucher details --- internal/handlers/ussd/menuhandler.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d8e6fa0..8f206e3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1626,10 +1626,9 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by return res, nil } - tokenSymbol := voucherData.TokenSymbol - tokenName := voucherData.TokenName - - res.Content = fmt.Sprintf("%s %s", tokenSymbol, tokenName) + res.Content = fmt.Sprintf( + "name: %s\nsymbol: %s\ncommodity: %s\nlocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation, + ) return res, nil } From 07df450b3c66e71caba203476e2678f880e57916 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 21 Nov 2024 15:15:15 +0300 Subject: [PATCH 181/332] include labels to define the symbol and balance while selecting a voucher --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index eeca3fd..bb63209 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1570,7 +1570,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - res.Content = fmt.Sprintf("%s\n%s", metadata.TokenSymbol, metadata.Balance) + res.Content = fmt.Sprintf("Symbol: %s\nBalance: %s", metadata.TokenSymbol, metadata.Balance) return res, nil } From 1174500e3fce4d0621924d936370eac2d63ea9ad Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 21 Nov 2024 15:19:36 +0300 Subject: [PATCH 182/332] add test for voucher details --- internal/handlers/ussd/menuhandler_test.go | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 51693df..2559a54 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2024,3 +2024,44 @@ func TestSetVoucher(t *testing.T) { assert.Equal(t, string(tempData.TokenSymbol), res.Content) } + +func TestGetVoucherDetails(t *testing.T) { + + ctx, store := InitializeTestStore(t) + fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Logf(err.Error()) + } + mockAccountService := new(mocks.MockAccountService) + + sessionId := "session123" + ctx = context.WithValue(ctx, "SessionId", sessionId) + expectedResult := resource.Result{} + + tokA_AAddress := "0x0000000000000000000000000000000000000000" + + h := &Handlers{ + userdataStore: store, + flagManager: fm.parser, + accountService: mockAccountService, + } + err = store.WriteEntry(ctx, sessionId, common.DATA_ACTIVE_ADDRESS, []byte(tokA_AAddress)) + if err != nil { + t.Fatal(err) + } + tokenDetails := &models.VoucherDataResult{ + TokenName: "Token A", + TokenSymbol: "TOKA", + TokenLocation: "Kilifi,Kenya", + TokenCommodity: "Farming", + } + expectedResult.Content = fmt.Sprintf( + "name: %s\nsymbol: %s\ncommodity: %s\nlocation: %s", tokenDetails.TokenName, tokenDetails.TokenSymbol, tokenDetails.TokenCommodity, tokenDetails.TokenLocation, + ) + mockAccountService.On("VoucherData", string(tokA_AAddress)).Return(tokenDetails, nil) + + res, err := h.GetVoucherDetails(ctx, "SessionId", []byte("")) + assert.NoError(t, err) + assert.Equal(t, expectedResult, res) + +} From b9ff467c0cb12b539c472c42a778e510a7570508 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 21 Nov 2024 15:51:04 +0300 Subject: [PATCH 183/332] use the correct balance --- services/registration/my_balance_swa | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/my_balance_swa b/services/registration/my_balance_swa index 9c3a7c7..afae8c1 100644 --- a/services/registration/my_balance_swa +++ b/services/registration/my_balance_swa @@ -1 +1 @@ -Salio lako ni: 0.00 SRF \ No newline at end of file +{{.check_balance}} \ No newline at end of file From e30bc177e96f0cba203c72c03065e854eb93a753 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 21 Nov 2024 15:52:07 +0300 Subject: [PATCH 184/332] fixed typo and added a new translation --- services/registration/locale/swa/default.po | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/services/registration/locale/swa/default.po b/services/registration/locale/swa/default.po index aa033b5..4bf876b 100644 --- a/services/registration/locale/swa/default.po +++ b/services/registration/locale/swa/default.po @@ -13,7 +13,7 @@ msgstr "Kwa usaidizi zaidi,piga: 0757628885" msgid "Balance: %s\n" msgstr "Salio: %s\n" -msid "Your invite request for %s to Sarafu Network failed. Please try again later." +msgid "Your invite request for %s to Sarafu Network failed. Please try again later." msgstr "Ombi lako la kumwalika %s kwa matandao wa Sarafu halikufaulu. Tafadhali jaribu tena baadaye." msgid "Your invitation to %s to join Sarafu Network has been sent." @@ -23,4 +23,7 @@ msgid "Your request failed. Please try again later." msgstr "Ombi lako halikufaulu. Tafadhali jaribu tena baadaye." msgid "Community Balance: 0.00" -msgid "Salio la Kikundi: 0.00" +msgstr "Salio la Kikundi: 0.00" + +msgid "Symbol: %s\nBalance: %s" +msgstr "Sarafu: %s\nSalio: %s" From 7ce50398d1b829535f59a6f0e60c5c1c1c822c2b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 21 Nov 2024 15:54:00 +0300 Subject: [PATCH 185/332] use the language translation instead of hardcoded eng --- internal/handlers/ussd/menuhandler.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index bb63209..98baf9d 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -817,7 +817,8 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - res.Content = l.Get("Balance: %s\n", fmt.Sprintf("%s %s", activeBal, activeSym)) + balStr := fmt.Sprintf("%s %s", activeBal, activeSym) + res.Content = l.Get("Balance: %s\n", balStr) return res, nil } @@ -1546,6 +1547,10 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, fmt.Errorf("missing session") } + code := codeFromCtx(ctx) + l := gotext.NewLocale(translationDir, code) + l.AddDomain("default") + flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher") inputStr := string(input) @@ -1570,7 +1575,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r } res.FlagReset = append(res.FlagReset, flag_incorrect_voucher) - res.Content = fmt.Sprintf("Symbol: %s\nBalance: %s", metadata.TokenSymbol, metadata.Balance) + res.Content = l.Get("Symbol: %s\nBalance: %s", metadata.TokenSymbol, metadata.Balance) return res, nil } From 9847433e0ad64cc33d216df5830e7fd925334ca7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 22 Nov 2024 11:30:13 +0300 Subject: [PATCH 186/332] use _ for back navigation --- services/registration/edit_profile.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index af20e0f..cca6be6 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -11,7 +11,7 @@ MOUT edit_offerings 6 MOUT view 7 MOUT back 0 HALT -INCMP my_account 0 +INCMP _ 0 INCMP edit_first_name 1 INCMP edit_family_name 2 INCMP select_gender 3 From a748c1b6b2f1ece736cbaf23ccf18524d769121b Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Fri, 22 Nov 2024 11:53:13 +0300 Subject: [PATCH 187/332] chore: fix old reference --- internal/testutil/testtag/onlinetest.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/testutil/testtag/onlinetest.go b/internal/testutil/testtag/onlinetest.go index 92cbb14..835ba0d 100644 --- a/internal/testutil/testtag/onlinetest.go +++ b/internal/testutil/testtag/onlinetest.go @@ -2,8 +2,8 @@ package testtag -import "git.grassecon.net/urdt/ussd/internal/handlers/server" +import "git.grassecon.net/urdt/ussd/remote" var ( - AccountService server.AccountServiceInterface + AccountService remote.AccountServiceInterface ) From 2d6c434bde0763e3187c1eac5cb8a7ba64ab92c7 Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Mon, 25 Nov 2024 09:56:24 +0300 Subject: [PATCH 188/332] feat: upgrade go-vise dep, minor cleanups to go.mod * added stub for Dump() --- cmd/africastalking/main.go | 10 +++++----- go.mod | 36 +++++++++++++++--------------------- go.sum | 8 ++++---- internal/storage/gdbm.go | 7 ++++++- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 9f5b501..7eb0497 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -44,14 +44,14 @@ type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { - log.Println("got an invalid request:", rq) + log.Printf("got an invalid request:", rq) return "", handlers.ErrInvalidRequest } // Capture body (if any) for logging body, err := io.ReadAll(rqv.Body) if err != nil { - log.Println("failed to read request body:", err) + log.Printf("failed to read request body:", err) return "", fmt.Errorf("failed to read request body: %v", err) } // Reset the body for further reading @@ -61,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { bodyLog := map[string]string{"body": string(body)} logBytes, err := json.Marshal(bodyLog) if err != nil { - log.Println("failed to marshal request body:", err) + log.Printf("failed to marshal request body:", err) } else { - log.Println("Received request:", string(logBytes)) + log.Printf("Received request:", string(logBytes)) } if err := rqv.ParseForm(); err != nil { - log.Println("failed to parse form data: %v", err) + log.Printf("failed to parse form data: %v", err) return "", fmt.Errorf("failed to parse form data: %v", err) } diff --git a/go.mod b/go.mod index 391c1a5..d89f0f3 100644 --- a/go.mod +++ b/go.mod @@ -2,29 +2,16 @@ module git.grassecon.net/urdt/ussd go 1.23.0 -toolchain go1.23.2 - require ( - git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b + git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a 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/peteole/testdata-loader v0.3.0 - gopkg.in/leonelquinteros/gotext.v1 v1.3.1 -) - -require github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a // indirect - -require ( - github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.1 // indirect - github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/grassrootseconomics/ussd-data-service v1.2.0-beta github.com/joho/godotenv v1.5.1 - github.com/kr/text v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/text v0.18.0 // indirect + github.com/peteole/testdata-loader v0.3.0 + github.com/stretchr/testify v1.9.0 + gopkg.in/leonelquinteros/gotext.v1 v1.3.1 ) require ( @@ -33,13 +20,20 @@ require ( 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/fxamacker/cbor/v2 v2.4.0 // indirect - github.com/gofrs/uuid v4.4.0+incompatible 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/pgx/v5 v5.7.1 // 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/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.9.0 github.com/x448/float16 v0.8.4 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/text v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0ba38c1..5532c1c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b h1:dxBplsIlzJHV+5EH+gzB+w08Blt7IJbb2jeRe1OEjLU= -git.defalsify.org/vise.git v0.2.1-0.20241017112704-307fa6fcdc6b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE= +git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= @@ -18,8 +18,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/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 v0.0.0-20241003123429-4904b4438a3a h1:q/YH7nE2j8epNmFnTu0tU1vwtCxtQ6nH+d7hRVV5krU= -github.com/grassrootseconomics/ussd-data-service v0.0.0-20241003123429-4904b4438a3a/go.mod h1:hdKaKwqiW6/kphK4j/BhmuRlZDLo1+DYo3gYw5O0siw= +github.com/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= diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go index 49de570..11979e1 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm.go @@ -4,8 +4,8 @@ import ( "context" "git.defalsify.org/vise.git/db" - "git.defalsify.org/vise.git/lang" gdbmdb "git.defalsify.org/vise.git/db/gdbm" + "git.defalsify.org/vise.git/lang" ) var ( @@ -114,3 +114,8 @@ func(tdb *ThreadGdbmDb) Close() error { tdb.db = nil return err } + +func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) { + logg.Warnf("method not implemented for thread gdbm db") + return nil, nil +} From 08ff1056d774433b6f72935751941aa993ba267a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 26 Nov 2024 07:24:57 +0100 Subject: [PATCH 189/332] Validate aliases, addresses and phone numbers in the send menu (#176) - Update the phone number regex - Check whether the recipient is a valid phone number, alias or address Reviewed-on: https://git.grassecon.net/urdt/ussd/pulls/176 Co-authored-by: alfred-mk Co-committed-by: alfred-mk --- cmd/africastalking/main.go | 9 +- common/recipient.go | 73 +++++++++++++ config/config.go | 3 + internal/handlers/ussd/menuhandler.go | 102 ++++++++++++------ internal/testutil/mocks/servicemock.go | 5 + .../testservice/TestAccountService.go | 8 +- menutraversal_test/test_setup.json | 4 +- remote/accountservice.go | 21 ++++ services/registration/send | 2 +- 9 files changed, 186 insertions(+), 41 deletions(-) create mode 100644 common/recipient.go diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 7eb0497..7d1caa8 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -20,6 +20,7 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" + "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" @@ -76,7 +77,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { return "", fmt.Errorf("no phone number found") } - return phoneNumber, nil + formattedNumber, err := common.FormatPhoneNumber(phoneNumber) + if err != nil { + fmt.Printf("Error: %v\n", err) + return "", fmt.Errorf("failed to format number") + } + + return formattedNumber, nil } func (arp *atRequestParser) GetInput(rq any) ([]byte, error) { diff --git a/common/recipient.go b/common/recipient.go new file mode 100644 index 0000000..f463a32 --- /dev/null +++ b/common/recipient.go @@ -0,0 +1,73 @@ +package common + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +// Define the regex patterns as constants +const ( + phoneRegex = `^(?:\+254|254|0)?((?:7[0-9]{8})|(?:1[01][0-9]{7}))$` + addressRegex = `^0x[a-fA-F0-9]{40}$` + aliasRegex = `^[a-zA-Z0-9]+$` +) + +// IsValidPhoneNumber checks if the given number is a valid phone number +func IsValidPhoneNumber(phonenumber string) bool { + match, _ := regexp.MatchString(phoneRegex, phonenumber) + return match +} + +// IsValidAddress checks if the given address is a valid Ethereum address +func IsValidAddress(address string) bool { + match, _ := regexp.MatchString(addressRegex, address) + return match +} + +// IsValidAlias checks if the alias is a valid alias format +func IsValidAlias(alias string) bool { + match, _ := regexp.MatchString(aliasRegex, alias) + return match +} + +// CheckRecipient validates the recipient format based on the criteria +func CheckRecipient(recipient string) (string, error) { + if IsValidPhoneNumber(recipient) { + return "phone number", nil + } + + if IsValidAddress(recipient) { + return "address", nil + } + + if IsValidAlias(recipient) { + return "alias", nil + } + + return "", fmt.Errorf("invalid recipient: must be a phone number, address or alias") +} + +// FormatPhoneNumber formats a Kenyan phone number to "+254xxxxxxxx". +func FormatPhoneNumber(phone string) (string, error) { + if !IsValidPhoneNumber(phone) { + return "", errors.New("invalid phone number") + } + + // Remove any leading "+" and spaces + phone = strings.TrimPrefix(phone, "+") + phone = strings.ReplaceAll(phone, " ", "") + + // Replace leading "0" with "254" if present + if strings.HasPrefix(phone, "0") { + phone = "254" + phone[1:] + } + + // Add "+" if not already present + if !strings.HasPrefix(phone, "254") { + return "", errors.New("unexpected format") + } + + return "+" + phone, nil +} diff --git a/config/config.go b/config/config.go index edb33fe..3a8e8ed 100644 --- a/config/config.go +++ b/config/config.go @@ -15,6 +15,7 @@ const ( voucherHoldingsPathPrefix = "/api/v1/holdings" voucherTransfersPathPrefix = "/api/v1/transfers/last10" voucherDataPathPrefix = "/api/v1/token" + AliasPrefix = "api/v1/alias" ) var ( @@ -32,6 +33,7 @@ var ( VoucherHoldingsURL string VoucherTransfersURL string VoucherDataURL string + CheckAliasURL string ) func setBase() error { @@ -66,6 +68,7 @@ func LoadConfig() error { VoucherHoldingsURL, _ = url.JoinPath(dataURLBase, voucherHoldingsPathPrefix) VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix) VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix) + CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix) return nil } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 065da85..8d3b928 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -35,12 +35,17 @@ var ( errResponse *api.ErrResponse ) -// Define the regex patterns as constants +// Define the regex patterns as constants const ( - phoneRegex = `(\(\d{3}\)\s?|\d{3}[-.\s]?)?\d{3}[-.\s]?\d{4}` pinPattern = `^\d{4}$` ) +// isValidPIN checks whether the given input is a 4 digit number +func isValidPIN(pin string) bool { + match, _ := regexp.MatchString(pinPattern, pin) + return match +} + // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser @@ -95,17 +100,6 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util return h, nil } -// isValidPIN checks whether the given input is a 4 digit number -func isValidPIN(pin string) bool { - match, _ := regexp.MatchString(pinPattern, pin) - return match -} - -func isValidPhoneNumber(phonenumber string) bool { - match, _ := regexp.MatchString(phoneRegex, phonenumber) - return match -} - func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { if h.pe != nil { panic("persister already set") @@ -877,7 +871,7 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input } blockedNumber := string(input) _, err = store.ReadEntry(ctx, blockedNumber, common.DATA_PUBLIC_KEY) - if !isValidPhoneNumber(blockedNumber) { + if !common.IsValidPhoneNumber(blockedNumber) { res.FlagSet = append(res.FlagSet, flag_unregistered_number) return res, nil } @@ -898,10 +892,9 @@ func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input return res, nil } -// ValidateRecipient validates that the given input is a valid phone number. +// ValidateRecipient validates that the given input is valid. func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - var err error store := h.userdataStore sessionId, ok := ctx.Value("SessionId").(string) @@ -909,13 +902,16 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, fmt.Errorf("missing session") } - recipient := string(input) - flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") + flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error") + + recipient := string(input) if recipient != "0" { - if !isValidPhoneNumber(recipient) { + recipientType, err := common.CheckRecipient(recipient) + if err != nil { + // Invalid recipient format (not a phone number, address, or valid alias format) res.FlagSet = append(res.FlagSet, flag_invalid_recipient) res.Content = recipient @@ -929,25 +925,61 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, err } - publicKey, err := store.ReadEntry(ctx, recipient, common.DATA_PUBLIC_KEY) - if err != nil { - if db.IsNotFound(err) { - logg.InfoCtxf(ctx, "Unregistered number") - - res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) - res.Content = recipient - - return res, nil + switch recipientType { + case "phone number": + // format the phone number + formattedNumber, err := common.FormatPhoneNumber(recipient) + if err != nil { + logg.ErrorCtxf(ctx, "Failed to format the phone number: %s", recipient, "error", err) + return res, err } - logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) - return res, err - } + // Check if the phone number is registered + publicKey, err := store.ReadEntry(ctx, formattedNumber, common.DATA_PUBLIC_KEY) + if err != nil { + if db.IsNotFound(err) { + logg.InfoCtxf(ctx, "Unregistered phone number: %s", recipient) + res.FlagSet = append(res.FlagSet, flag_invalid_recipient_with_invite) + res.Content = recipient + return res, nil + } - err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err) - return res, nil + logg.ErrorCtxf(ctx, "failed to read publicKey entry with", "key", common.DATA_PUBLIC_KEY, "error", err) + return res, err + } + + // Save the publicKey as the recipient + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, publicKey) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", string(publicKey), "error", err) + return res, err + } + + case "address": + // Save the valid Ethereum address as the recipient + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(recipient)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", recipient, "error", err) + return res, err + } + + case "alias": + // Call the API to validate and retrieve the address for the alias + r, aliasErr := h.accountService.CheckAliasAddress(ctx, recipient) + if aliasErr != nil { + res.FlagSet = append(res.FlagSet, flag_api_error) + res.Content = recipient + + logg.ErrorCtxf(ctx, "failed on CheckAliasAddress", "error", aliasErr) + return res, err + } + + // Alias validation succeeded, save the Ethereum address + err = store.WriteEntry(ctx, sessionId, common.DATA_RECIPIENT, []byte(r.Address)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write recipient entry with", "key", common.DATA_RECIPIENT, "value", r.Address, "error", err) + return res, err + } } } diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index a25f1ae..521cfa0 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -47,3 +47,8 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to args := m.Called() return args.Get(0).(*models.TokenTransferResponse), args.Error(1) } + +func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + args := m.Called() + return args.Get(0).(*dataserviceapi.AliasAddress), args.Error(1) +} diff --git a/internal/testutil/testservice/TestAccountService.go b/internal/testutil/testservice/TestAccountService.go index 7c486f3..96dacbc 100644 --- a/internal/testutil/testservice/TestAccountService.go +++ b/internal/testutil/testservice/TestAccountService.go @@ -33,8 +33,8 @@ func (tas *TestAccountService) TrackAccountStatus(ctx context.Context, publicKey } func (tas *TestAccountService) FetchVouchers(ctx context.Context, publicKey string) ([]dataserviceapi.TokenHoldings, error) { - return []dataserviceapi.TokenHoldings { - dataserviceapi.TokenHoldings { + return []dataserviceapi.TokenHoldings{ + dataserviceapi.TokenHoldings{ ContractAddress: "0x6CC75A06ac72eB4Db2eE22F781F5D100d8ec03ee", TokenSymbol: "SRF", TokenDecimals: "6", @@ -56,3 +56,7 @@ func (tas *TestAccountService) TokenTransfer(ctx context.Context, amount, from, TrackingId: "e034d147-747d-42ea-928d-b5a7cb3426af", }, nil } + +func (m TestAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + return &dataserviceapi.AliasAddress{}, nil +} diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index a554939..4d8414f 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -61,7 +61,7 @@ }, { "input": "1", - "expectedContent": "Enter recipient's phone number:\n0:Back" + "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" }, { "input": "000", @@ -69,7 +69,7 @@ }, { "input": "1", - "expectedContent": "Enter recipient's phone number:\n0:Back" + "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" }, { "input": "0712345678", diff --git a/remote/accountservice.go b/remote/accountservice.go index 23b62ca..b0e9eb4 100644 --- a/remote/accountservice.go +++ b/remote/accountservice.go @@ -24,6 +24,7 @@ type AccountServiceInterface interface { FetchTransactions(ctx context.Context, publicKey string) ([]dataserviceapi.Last10TxResponse, error) VoucherData(ctx context.Context, address string) (*models.VoucherDataResult, error) TokenTransfer(ctx context.Context, amount, from, to, tokenAddress string) (*models.TokenTransferResponse, error) + CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) } type AccountService struct { @@ -209,6 +210,26 @@ func (as *AccountService) TokenTransfer(ctx context.Context, amount, from, to, t return &r, nil } +// CheckAliasAddress retrieves the address of an alias from the API endpoint. +// Parameters: +// - alias: The alias of the user. +func (as *AccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { + var r dataserviceapi.AliasAddress + + ep, err := url.JoinPath(config.CheckAliasURL, alias) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", ep, nil) + if err != nil { + return nil, err + } + + _, err = doRequest(ctx, req, &r) + return &r, err +} + func doRequest(ctx context.Context, req *http.Request, rcpt any) (*api.OKResponse, error) { var okResponse api.OKResponse var errResponse api.ErrResponse diff --git a/services/registration/send b/services/registration/send index d124026..306466c 100644 --- a/services/registration/send +++ b/services/registration/send @@ -1 +1 @@ -Enter recipient's phone number: \ No newline at end of file +Enter recipient's phone number/address/alias: \ No newline at end of file From 00c0445eed5e0e437907d90d125702194fbf2c92 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 28 Nov 2024 11:28:00 +0300 Subject: [PATCH 190/332] show name without depending on family name being set --- internal/handlers/ussd/menuhandler.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a0b7bd1..dfd046d 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1391,10 +1391,14 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) // Construct the full name name := defaultValue if familyName != defaultValue { - if firstName == defaultValue { - name = familyName - } else { + if firstName != defaultValue { name = firstName + " " + familyName + } else { + name = familyName + } + } else { + if firstName != defaultValue { + name = firstName } } From c46f41e25f1a213e4d9ef0c0abcee2ee20f0256c Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 29 Nov 2024 14:47:22 +0300 Subject: [PATCH 191/332] Format the balance to 2 decimal places --- internal/handlers/ussd/menuhandler.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index dfd046d..bb0c9d8 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -811,7 +811,16 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, err } - balStr := fmt.Sprintf("%s %s", activeBal, activeSym) + // Convert activeBal from []byte to float64 + balFloat, err := strconv.ParseFloat(string(activeBal), 64) + if err != nil { + logg.ErrorCtxf(ctx, "failed to parse activeBal as float", "value", string(activeBal), "error", err) + return res, err + } + + // Format to 2 decimal places + balStr := fmt.Sprintf("%.2f %s", balFloat, activeSym) + res.Content = l.Get("Balance: %s\n", balStr) return res, nil From aab6660edd2ac4b6004f50aeb1ee19d788c8e58b Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 29 Nov 2024 15:39:27 +0300 Subject: [PATCH 192/332] Capitalize menu items --- menutraversal_test/group_test.json | 8 ++++---- menutraversal_test/test_setup.json | 8 ++++---- services/registration/english_menu | 1 + services/registration/incorrect_pin | 2 +- services/registration/invalid_pin_swa | 2 +- services/registration/kiswahili_menu | 1 + services/registration/next_menu | 1 + services/registration/next_menu_swa | 1 + services/registration/no_menu | 2 +- services/registration/no_menu_swa | 2 +- services/registration/prev_menu | 1 + services/registration/prev_menu_swa | 1 + services/registration/yes_menu | 2 +- services/registration/yes_menu_swa | 2 +- 14 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 services/registration/english_menu create mode 100644 services/registration/kiswahili_menu create mode 100644 services/registration/next_menu create mode 100644 services/registration/next_menu_swa create mode 100644 services/registration/prev_menu create mode 100644 services/registration/prev_menu_swa diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 52fab59..7985b22 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -54,7 +54,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" + "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" }, { "input": "1", @@ -62,7 +62,7 @@ }, { "input": "1234", - "expectedContent": "Select language:\n0:english\n1:kiswahili" + "expectedContent": "Select language:\n0:English\n1:Kiswahili" }, { "input": "0", @@ -95,7 +95,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" + "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" }, { "input": "1", @@ -141,7 +141,7 @@ }, { "input": "1235", - "expectedContent": "Incorrect pin\n1:Retry\n9:Quit" + "expectedContent": "Incorrect PIN\n1:Retry\n9:Quit" }, { "input": "1", diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 4d8414f..1f9d61d 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -7,11 +7,11 @@ "steps": [ { "input": "", - "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili" }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" + "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No" }, { "input": "0", @@ -40,11 +40,11 @@ "steps": [ { "input": "", - "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:english\n1:kiswahili" + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili" }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\n0:yes\n1:no" + "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No" }, { "input": "1", diff --git a/services/registration/english_menu b/services/registration/english_menu new file mode 100644 index 0000000..3d38949 --- /dev/null +++ b/services/registration/english_menu @@ -0,0 +1 @@ +English \ No newline at end of file diff --git a/services/registration/incorrect_pin b/services/registration/incorrect_pin index d11ab54..7fcf610 100644 --- a/services/registration/incorrect_pin +++ b/services/registration/incorrect_pin @@ -1 +1 @@ -Incorrect pin \ No newline at end of file +Incorrect PIN \ No newline at end of file diff --git a/services/registration/invalid_pin_swa b/services/registration/invalid_pin_swa index 1817570..7512242 100644 --- a/services/registration/invalid_pin_swa +++ b/services/registration/invalid_pin_swa @@ -1 +1 @@ -PIN mpya na udhibitisho wa pin mpya hazilingani.Tafadhali jaribu tena.Kwa usaidizi piga simu +254757628885. +PIN mpya na udhibitisho wa PIN mpya hazilingani.Tafadhali jaribu tena.Kwa usaidizi piga simu +254757628885. diff --git a/services/registration/kiswahili_menu b/services/registration/kiswahili_menu new file mode 100644 index 0000000..e4d88a5 --- /dev/null +++ b/services/registration/kiswahili_menu @@ -0,0 +1 @@ +Kiswahili \ No newline at end of file diff --git a/services/registration/next_menu b/services/registration/next_menu new file mode 100644 index 0000000..e2e838e --- /dev/null +++ b/services/registration/next_menu @@ -0,0 +1 @@ +Next \ No newline at end of file diff --git a/services/registration/next_menu_swa b/services/registration/next_menu_swa new file mode 100644 index 0000000..6511e40 --- /dev/null +++ b/services/registration/next_menu_swa @@ -0,0 +1 @@ +Mbele \ No newline at end of file diff --git a/services/registration/no_menu b/services/registration/no_menu index 54299a4..289cc91 100644 --- a/services/registration/no_menu +++ b/services/registration/no_menu @@ -1 +1 @@ -no \ No newline at end of file +No \ No newline at end of file diff --git a/services/registration/no_menu_swa b/services/registration/no_menu_swa index 3e6885e..a9d6b8d 100644 --- a/services/registration/no_menu_swa +++ b/services/registration/no_menu_swa @@ -1 +1 @@ -la \ No newline at end of file +La \ No newline at end of file diff --git a/services/registration/prev_menu b/services/registration/prev_menu new file mode 100644 index 0000000..72d90d8 --- /dev/null +++ b/services/registration/prev_menu @@ -0,0 +1 @@ +Prev \ No newline at end of file diff --git a/services/registration/prev_menu_swa b/services/registration/prev_menu_swa new file mode 100644 index 0000000..e5a3e45 --- /dev/null +++ b/services/registration/prev_menu_swa @@ -0,0 +1 @@ +Nyuma \ No newline at end of file diff --git a/services/registration/yes_menu b/services/registration/yes_menu index 396a0ba..3fdfb3d 100644 --- a/services/registration/yes_menu +++ b/services/registration/yes_menu @@ -1 +1 @@ -yes \ No newline at end of file +Yes \ No newline at end of file diff --git a/services/registration/yes_menu_swa b/services/registration/yes_menu_swa index c5231fb..542d3c3 100644 --- a/services/registration/yes_menu_swa +++ b/services/registration/yes_menu_swa @@ -1 +1 @@ -ndio \ No newline at end of file +Ndio \ No newline at end of file From 6b23c284e5c72bf7f1e517b31a3cb46fa256b1cd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 30 Nov 2024 15:24:14 +0300 Subject: [PATCH 193/332] check vouchers before checking the balance --- services/registration/main.vis | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/registration/main.vis b/services/registration/main.vis index 7e1c9bf..beb2d7c 100644 --- a/services/registration/main.vis +++ b/services/registration/main.vis @@ -1,10 +1,10 @@ LOAD set_default_voucher 8 RELOAD set_default_voucher -LOAD check_balance 64 -RELOAD check_balance LOAD check_vouchers 10 RELOAD check_vouchers -CATCH api_failure flag_api_call_error 1 +LOAD check_balance 64 +RELOAD check_balance +CATCH api_failure flag_api_call_error 1 MAP check_balance MOUT send 1 MOUT vouchers 2 From 7a86b2ad3bb6b310b13f3faeda7bde9f587a16ea Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 30 Nov 2024 15:26:13 +0300 Subject: [PATCH 194/332] updated the UpdateVoucherData description --- common/vouchers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/vouchers.go b/common/vouchers.go index 81ca55f..c542084 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -151,7 +151,7 @@ func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId str return data, nil } -// UpdateVoucherData sets the active voucher data in the DataStore. +// UpdateVoucherData updates the active voucher data in the DataStore. func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *dataserviceapi.TokenHoldings) error { logg.TraceCtxf(ctx, "dtal", "data", data) // Active voucher data entries From 54c1fe51ef66e66a5a927b50c7bc99bc48b0aec6 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 30 Nov 2024 15:28:21 +0300 Subject: [PATCH 195/332] update the active voucher data when checking the current vouchers --- internal/handlers/ussd/menuhandler.go | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8d3b928..4b7fc7a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -25,6 +25,7 @@ import ( "gopkg.in/leonelquinteros/gotext.v1" "git.grassecon.net/urdt/ussd/internal/storage" + dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) var ( @@ -1534,6 +1535,38 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } + // check the current active sym and update the data + activeSym, _ := store.ReadEntry(ctx, sessionId, common.DATA_ACTIVE_SYM) + if activeSym != nil { + activeSymStr := string(activeSym) + + // Find the matching voucher data + var activeData *dataserviceapi.TokenHoldings + for _, voucher := range vouchersResp { + if voucher.TokenSymbol == activeSymStr { + activeData = &voucher + break + } + } + + if activeData == nil { + logg.ErrorCtxf(ctx, "activeSym not found in vouchers", "activeSym", activeSymStr) + return res, fmt.Errorf("activeSym %s not found in vouchers", activeSymStr) + } + + // Scale down the balance + scaledBalance := common.ScaleDownBalance(activeData.Balance, activeData.TokenDecimals) + + // Update the balance field with the scaled value + activeData.Balance = scaledBalance + + // Pass the matching voucher data to UpdateVoucherData + if err := common.UpdateVoucherData(ctx, h.userdataStore, sessionId, activeData); err != nil { + logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err) + return res, err + } + } + data := common.ProcessVouchers(vouchersResp) // Store all voucher data From aa7497573e7c0d07eebd6d1e278c7af3ea34825d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 30 Nov 2024 15:29:28 +0300 Subject: [PATCH 196/332] removed unused code --- internal/handlers/ussd/menuhandler.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4b7fc7a..ff9d11e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -10,7 +10,6 @@ import ( "strings" "git.defalsify.org/vise.git/asm" - "github.com/grassrootseconomics/eth-custodial/pkg/api" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/db" @@ -32,8 +31,6 @@ var ( logg = logging.NewVanilla().WithDomain("ussdmenuhandler") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") - okResponse *api.OKResponse - errResponse *api.ErrResponse ) // Define the regex patterns as constants From 1a782c1db9b9d0339435f6e47d8c20e73ff23b7a Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 1 Dec 2024 17:32:06 +0000 Subject: [PATCH 197/332] Add debug package --- debug/db.go | 47 ++++++++++++++++++++++++++++++++++++++++++ debug/db_no.go | 14 +++++++++++++ debug/db_yes.go | 42 +++++++++++++++++++++++++++++++++++++ devtools/store/main.go | 3 ++- 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 debug/db.go create mode 100644 debug/db_no.go create mode 100644 debug/db_yes.go diff --git a/debug/db.go b/debug/db.go new file mode 100644 index 0000000..9efad00 --- /dev/null +++ b/debug/db.go @@ -0,0 +1,47 @@ +package debug + +import ( + "fmt" + "encoding/binary" + + "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/common" +) + +type KeyInfo struct { + SessionId string + Typ uint8 + SubTyp common.DataTyp + Label string + Description string +} + +func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { + o := KeyInfo{} + b := []byte(sessionId) + if len(k) <= len(b) { + return o, fmt.Errorf("storage key missing") + } + + o.SessionId = sessionId + + k = k[len(b):] + o.Typ = k[0] + k = k[1:] + + if o.Typ == storage.DATATYPE_USERSUB { + if len(k) == 0 { + return o, fmt.Errorf("missing subtype key") + } + v := binary.BigEndian.Uint16(k[:2]) + o.SubTyp = common.DataTyp(v) + o.Label = typToString(o.SubTyp) + k = k[2:] + } + + if len(k) != 0 { + return o, fmt.Errorf("excess key information") + } + + return o, nil +} diff --git a/debug/db_no.go b/debug/db_no.go new file mode 100644 index 0000000..a01c78f --- /dev/null +++ b/debug/db_no.go @@ -0,0 +1,14 @@ +// +build !debugdb + +package debug + +import ( + "fmt" + + "git.grassecon.net/urdt/ussd/common" +) + + +func typToString(v common.DataTyp) string { + return fmt.Sprintf("(%d)", v) +} diff --git a/debug/db_yes.go b/debug/db_yes.go new file mode 100644 index 0000000..fa8186e --- /dev/null +++ b/debug/db_yes.go @@ -0,0 +1,42 @@ +// +build debugdb + +package debug + +import ( + "git.defalsify.org/vise.git/db" + + "git.grassecon.net/urdt/ussd/common" +) + +var ( + dbTypStr map[common.DataTyp]string = map[common.DataTyp]string { + db.DATATYPE_USERDATA: "userdata", + common.DATA_ACCOUNT: "account", + common.DATA_ACCOUNT_CREATED: "account created", + common.DATA_TRACKING_ID: "tracking id", + common.DATA_PUBLIC_KEY: "public key", + common.DATA_CUSTODIAL_ID: "custodial id", + common.DATA_ACCOUNT_PIN: "account pin", + common.DATA_ACCOUNT_STATUS: "account status", + common.DATA_FIRST_NAME: "first name", + common.DATA_FAMILY_NAME: "family name", + common.DATA_YOB: "year of birth", + common.DATA_LOCATION: "location", + common.DATA_GENDER: "gender", + common.DATA_OFFERINGS: "offerings", + common.DATA_RECIPIENT: "recipient", + common.DATA_AMOUNT: "amount", + common.DATA_TEMPORARY_VALUE: "temporary value", + common.DATA_ACTIVE_SYM: "active sym", + common.DATA_ACTIVE_BAL: "active bal", + common.DATA_BLOCKED_NUMBER: "blocked number", + common.DATA_PUBLIC_KEY_REVERSE: "public key reverse", + common.DATA_ACTIVE_DECIMAL: "active decimal", + common.DATA_ACTIVE_ADDRESS: "active address", + common.DATA_TRANSACTIONS: "transactions", + } +) + +func typToString(v common.DataTyp) string { + return dbTypStr[v] +} diff --git a/devtools/store/main.go b/devtools/store/main.go index df27bb1..35587ea 100644 --- a/devtools/store/main.go +++ b/devtools/store/main.go @@ -22,6 +22,7 @@ func init() { initializers.LoadEnvVariables() } + func main() { config.LoadConfig() @@ -59,6 +60,6 @@ func main() { if k == nil { break } - fmt.Printf("%x %s\n", k, v) + fmt.Printf("%x %s %x\n", k, v) } } From 35cf3a1cd1d0586aadbb0dc68c43bab09a966bae Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 1 Dec 2024 18:41:28 +0000 Subject: [PATCH 198/332] Add debug label test, capability debug flag --- debug/cap.go | 5 +++++ debug/db_yes.go | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 debug/cap.go diff --git a/debug/cap.go b/debug/cap.go new file mode 100644 index 0000000..458bb48 --- /dev/null +++ b/debug/cap.go @@ -0,0 +1,5 @@ +package debug + +var ( + DebugCap uint32 +) diff --git a/debug/db_yes.go b/debug/db_yes.go index fa8186e..c323366 100644 --- a/debug/db_yes.go +++ b/debug/db_yes.go @@ -37,6 +37,10 @@ var ( } ) +func init() { + DebugCap |= 1 +} + func typToString(v common.DataTyp) string { return dbTypStr[v] } From 6d4f3109f8b312dc060f4c8c9d2b6f4791427b1a Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 1 Dec 2024 23:12:58 +0000 Subject: [PATCH 199/332] Consolidate subtyp and typ debug --- debug/db.go | 17 +++++++++++++++- debug/db_debug.go | 39 ++++++++++++++++++++++++++++++++++++ debug/db_no.go | 14 ------------- debug/db_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ debug/db_yes.go | 46 ------------------------------------------- 5 files changed, 105 insertions(+), 61 deletions(-) create mode 100644 debug/db_debug.go delete mode 100644 debug/db_no.go create mode 100644 debug/db_test.go delete mode 100644 debug/db_yes.go diff --git a/debug/db.go b/debug/db.go index 9efad00..ccc8630 100644 --- a/debug/db.go +++ b/debug/db.go @@ -8,6 +8,10 @@ import ( "git.grassecon.net/urdt/ussd/common" ) +var ( + dbTypStr map[common.DataTyp]string = make(map[common.DataTyp]string) +) + type KeyInfo struct { SessionId string Typ uint8 @@ -19,6 +23,7 @@ type KeyInfo struct { func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { o := KeyInfo{} b := []byte(sessionId) + if len(k) <= len(b) { return o, fmt.Errorf("storage key missing") } @@ -35,8 +40,10 @@ func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { } v := binary.BigEndian.Uint16(k[:2]) o.SubTyp = common.DataTyp(v) - o.Label = typToString(o.SubTyp) + o.Label = subTypToString(o.SubTyp) k = k[2:] + } else { + o.Label = typToString(o.Typ) } if len(k) != 0 { @@ -45,3 +52,11 @@ func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { return o, nil } + +func subTypToString(v common.DataTyp) string { + return dbTypStr[v + storage.DATATYPE_USERSUB + 1] +} + +func typToString(v uint8) string { + return dbTypStr[common.DataTyp(uint16(v))] +} diff --git a/debug/db_debug.go b/debug/db_debug.go new file mode 100644 index 0000000..e4a3949 --- /dev/null +++ b/debug/db_debug.go @@ -0,0 +1,39 @@ +// +build debugdb + +package debug + +import ( + "git.defalsify.org/vise.git/db" + + "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/internal/storage" +) + +func init() { + DebugCap |= 1 + dbTypStr[db.DATATYPE_STATE] = "internal_state" + dbTypStr[db.DATATYPE_USERDATA] = "userdata" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT] = "account" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_CREATED] = "account_created" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TRACKING_ID] = "tracking id" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_PUBLIC_KEY] = "public key" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_PIN] = "account pin" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_STATUS] = "account status" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_FIRST_NAME] = "first name" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_FAMILY_NAME] = "family name" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_YOB] = "year of birth" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_LOCATION] = "location" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_GENDER] = "gender" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_OFFERINGS] = "offerings" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_RECIPIENT] = "recipient" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_AMOUNT] = "amount" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_SYM] = "active sym" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_BAL] = "active bal" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_ADDRESS] = "active address" + dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TRANSACTIONS] = "transactions" +} diff --git a/debug/db_no.go b/debug/db_no.go deleted file mode 100644 index a01c78f..0000000 --- a/debug/db_no.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !debugdb - -package debug - -import ( - "fmt" - - "git.grassecon.net/urdt/ussd/common" -) - - -func typToString(v common.DataTyp) string { - return fmt.Sprintf("(%d)", v) -} diff --git a/debug/db_test.go b/debug/db_test.go new file mode 100644 index 0000000..2dae1e8 --- /dev/null +++ b/debug/db_test.go @@ -0,0 +1,50 @@ +package debug + +import ( + "testing" +) + +func TestDebugDbSubKeyInfo(t *testing.T) { + s := "foo" + b := []byte(s) + b = append(b, []byte{0x40, 0x00, 0x02}...) + r, err := ToKeyInfo(b, s) + if err != nil { + t.Fatal(err) + } + if r.SessionId != s { + t.Fatalf("expected %s, got %s", s, r.SessionId) + } + if r.Typ != 64 { + t.Fatalf("expected 64, got %d", r.Typ) + } + if r.SubTyp != 2 { + t.Fatalf("expected 2, got %d", r.SubTyp) + } + if DebugCap & 1 > 0 { + if r.Label != "tracking id" { + t.Fatalf("expected 'tracking id', got '%s'", r.Label) + } + } +} + +func TestDebugDbKeyInfo(t *testing.T) { + s := "bar" + b := []byte(s) + b = append(b, []byte{0x20}...) + r, err := ToKeyInfo(b, s) + if err != nil { + t.Fatal(err) + } + if r.SessionId != s { + t.Fatalf("expected %s, got %s", s, r.SessionId) + } + if r.Typ != 32 { + t.Fatalf("expected 64, got %d", r.Typ) + } + if DebugCap & 1 > 0 { + if r.Label != "userdata" { + t.Fatalf("expected 'userdata', got '%s'", r.Label) + } + } +} diff --git a/debug/db_yes.go b/debug/db_yes.go deleted file mode 100644 index c323366..0000000 --- a/debug/db_yes.go +++ /dev/null @@ -1,46 +0,0 @@ -// +build debugdb - -package debug - -import ( - "git.defalsify.org/vise.git/db" - - "git.grassecon.net/urdt/ussd/common" -) - -var ( - dbTypStr map[common.DataTyp]string = map[common.DataTyp]string { - db.DATATYPE_USERDATA: "userdata", - common.DATA_ACCOUNT: "account", - common.DATA_ACCOUNT_CREATED: "account created", - common.DATA_TRACKING_ID: "tracking id", - common.DATA_PUBLIC_KEY: "public key", - common.DATA_CUSTODIAL_ID: "custodial id", - common.DATA_ACCOUNT_PIN: "account pin", - common.DATA_ACCOUNT_STATUS: "account status", - common.DATA_FIRST_NAME: "first name", - common.DATA_FAMILY_NAME: "family name", - common.DATA_YOB: "year of birth", - common.DATA_LOCATION: "location", - common.DATA_GENDER: "gender", - common.DATA_OFFERINGS: "offerings", - common.DATA_RECIPIENT: "recipient", - common.DATA_AMOUNT: "amount", - common.DATA_TEMPORARY_VALUE: "temporary value", - common.DATA_ACTIVE_SYM: "active sym", - common.DATA_ACTIVE_BAL: "active bal", - common.DATA_BLOCKED_NUMBER: "blocked number", - common.DATA_PUBLIC_KEY_REVERSE: "public key reverse", - common.DATA_ACTIVE_DECIMAL: "active decimal", - common.DATA_ACTIVE_ADDRESS: "active address", - common.DATA_TRANSACTIONS: "transactions", - } -) - -func init() { - DebugCap |= 1 -} - -func typToString(v common.DataTyp) string { - return dbTypStr[v] -} From 0091fbcabbc5d9e6e6d91bdbb6218e888d542496 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Dec 2024 08:55:24 +0300 Subject: [PATCH 200/332] fix: looped navigation --- services/registration/edit_profile.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index cca6be6..af20e0f 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -11,7 +11,7 @@ MOUT edit_offerings 6 MOUT view 7 MOUT back 0 HALT -INCMP _ 0 +INCMP my_account 0 INCMP edit_first_name 1 INCMP edit_family_name 2 INCMP select_gender 3 From c2019267d1a77f55a4ae6f373416a5c80a93ef36 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 2 Dec 2024 13:26:46 +0300 Subject: [PATCH 201/332] capitalize the voucher descriptions --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8f206e3..5bc95bc 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1627,7 +1627,7 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by } res.Content = fmt.Sprintf( - "name: %s\nsymbol: %s\ncommodity: %s\nlocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation, + "Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation, ) return res, nil From ef3a3d6717c98a17516281e04f271c8fb6e712fd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 2 Dec 2024 13:30:33 +0300 Subject: [PATCH 202/332] updated the TestGetVoucherDetails --- internal/handlers/ussd/menuhandler_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 2559a54..6f1d57a 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2026,7 +2026,6 @@ func TestSetVoucher(t *testing.T) { } func TestGetVoucherDetails(t *testing.T) { - ctx, store := InitializeTestStore(t) fm, err := NewFlagManager(flagsPath) if err != nil { @@ -2056,12 +2055,11 @@ func TestGetVoucherDetails(t *testing.T) { TokenCommodity: "Farming", } expectedResult.Content = fmt.Sprintf( - "name: %s\nsymbol: %s\ncommodity: %s\nlocation: %s", tokenDetails.TokenName, tokenDetails.TokenSymbol, tokenDetails.TokenCommodity, tokenDetails.TokenLocation, + "Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", tokenDetails.TokenName, tokenDetails.TokenSymbol, tokenDetails.TokenCommodity, tokenDetails.TokenLocation, ) mockAccountService.On("VoucherData", string(tokA_AAddress)).Return(tokenDetails, nil) res, err := h.GetVoucherDetails(ctx, "SessionId", []byte("")) assert.NoError(t, err) assert.Equal(t, expectedResult, res) - } From c9deca11805f82c023d7026e8b37978ab319566c Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 2 Dec 2024 14:36:33 +0000 Subject: [PATCH 203/332] change userdata prefix on subprefix debug --- debug/db_debug.go | 47 +++++++++++++++++++++--------------------- devtools/store/main.go | 6 ++++++ 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/debug/db_debug.go b/debug/db_debug.go index e4a3949..7c17eed 100644 --- a/debug/db_debug.go +++ b/debug/db_debug.go @@ -12,28 +12,27 @@ import ( func init() { DebugCap |= 1 dbTypStr[db.DATATYPE_STATE] = "internal_state" - dbTypStr[db.DATATYPE_USERDATA] = "userdata" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT] = "account" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_CREATED] = "account_created" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TRACKING_ID] = "tracking id" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_PUBLIC_KEY] = "public key" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_PIN] = "account pin" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACCOUNT_STATUS] = "account status" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_FIRST_NAME] = "first name" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_FAMILY_NAME] = "family name" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_YOB] = "year of birth" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_LOCATION] = "location" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_GENDER] = "gender" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_OFFERINGS] = "offerings" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_RECIPIENT] = "recipient" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_AMOUNT] = "amount" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_SYM] = "active sym" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_BAL] = "active bal" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_ACTIVE_ADDRESS] = "active address" - dbTypStr[storage.DATATYPE_USERSUB + 1 + common.DATA_TRANSACTIONS] = "transactions" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account_created" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_LOCATION] = "location" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_GENDER] = "gender" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_OFFERINGS] = "offerings" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_RECIPIENT] = "recipient" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_AMOUNT] = "amount" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_SYM] = "active sym" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_BAL] = "active bal" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_ADDRESS] = "active address" + dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TRANSACTIONS] = "transactions" } diff --git a/devtools/store/main.go b/devtools/store/main.go index 35587ea..d6abec9 100644 --- a/devtools/store/main.go +++ b/devtools/store/main.go @@ -62,4 +62,10 @@ func main() { } fmt.Printf("%x %s %x\n", k, v) } + + err = store.Close() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } } From 406bd84875c7828e67942a0330ff5ecd7383b42c Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 2 Dec 2024 14:53:18 +0000 Subject: [PATCH 204/332] Always reset persister in handler --- internal/handlers/ussd/menuhandler.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 87760bd..a93d637 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -114,6 +114,9 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource logg.WarnCtxf(ctx, "handler init called before it is ready or more than once", "state", h.st, "cache", h.ca) return r, nil } + defer func() { + h.pe = nil + }() h.st = h.pe.GetState() h.ca = h.pe.GetMemory() @@ -133,7 +136,6 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource logg.ErrorCtxf(ctx, "perister fail in handler", "state", h.st, "cache", h.ca) return r, fmt.Errorf("cannot get state and memory for handler") } - h.pe = nil logg.DebugCtxf(ctx, "handler has been initialized", "state", h.st, "cache", h.ca) From fd586d09c3f80668cb8588f4b03b3d466ffaa300 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Dec 2024 20:35:04 +0300 Subject: [PATCH 205/332] add required profile edit flags --- services/registration/pp.csv | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index cd6a633..6c74b95 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -21,3 +21,9 @@ flag,flag_admin_privilege,27,this is set when a user has admin privileges. flag,flag_unregistered_number,28,this is set when an unregistered phonenumber tries to perform an action flag,flag_no_transfers,29,this is set when a user does not have any transactions flag,flag_incorrect_statement,30,this is set when the selected statement is invalid +flag,flag_firstname_set,31,this is set when the first name of the profile is set +flag,flag_familyname_set,32,this is set when the family name of the profile is set +flag,flag_yob_set,33,this is set when the yob of the profile is set +flag,flag_gender_set,34,this is set when the gender of the profile is set +flag,flag_location_set,35,this is set when the location of the profile is set +flag,flag_offerings_set,36,this is set when the offerings of the profile is set From 3e0bbe5ffea209de8a9414ad466c7d62a4e1e8fb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Dec 2024 20:35:56 +0300 Subject: [PATCH 206/332] add handler to update profile items --- internal/handlers/handlerservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index c77e82c..13ca70a 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -121,6 +121,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("check_transactions", ussdHandlers.CheckTransactions) ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList) ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) + ls.DbRs.AddLocalFunc("update_all_profile_items", ussdHandlers.UpdateAllProfileItems) return ussdHandlers, nil } From 48e1b02e0e7ef1ec758a7ab1a3c08c34351d587a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Dec 2024 20:50:21 +0300 Subject: [PATCH 207/332] allow all item profile edit --- services/registration/edit_family_name.vis | 7 ++++--- services/registration/edit_first_name.vis | 6 +++--- services/registration/edit_location.vis | 5 +++-- services/registration/edit_offerings.vis | 5 +++-- services/registration/edit_profile.vis | 2 +- services/registration/edit_yob.vis | 6 ++++-- services/registration/profile_update_success.vis | 2 ++ services/registration/set_female.vis | 5 +++-- services/registration/set_male.vis | 5 +++-- services/registration/set_unspecified.vis | 5 +++-- services/registration/update_profile_items.vis | 3 +++ 11 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 services/registration/update_profile_items.vis diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis index 1d71939..adcede1 100644 --- a/services/registration/edit_family_name.vis +++ b/services/registration/edit_family_name.vis @@ -1,10 +1,11 @@ -CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_familyname flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info +MAP get_current_profile_info MOUT back 0 +LOAD save_familyname 64 HALT -LOAD save_familyname 0 RELOAD save_familyname INCMP _ 0 -INCMP pin_entry * +CATCH pin_entry flag_familyname_set 1 +INCMP select_gender * diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis index b8dab5a..d230945 100644 --- a/services/registration/edit_first_name.vis +++ b/services/registration/edit_first_name.vis @@ -1,11 +1,11 @@ -CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_firstname flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info MAP get_current_profile_info +LOAD save_firstname 128 MOUT back 0 HALT -LOAD save_firstname 0 RELOAD save_firstname INCMP _ 0 -INCMP pin_entry * +CATCH pin_entry flag_firstname_set 1 +INCMP edit_family_name * diff --git a/services/registration/edit_location.vis b/services/registration/edit_location.vis index eaf248a..c9bc12d 100644 --- a/services/registration/edit_location.vis +++ b/services/registration/edit_location.vis @@ -2,9 +2,10 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_location flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info +LOAD save_location 16 MOUT back 0 HALT -LOAD save_location 0 RELOAD save_location INCMP _ 0 -INCMP pin_entry * +CATCH pin_entry flag_location_set 1 +INCMP edit_offerings * diff --git a/services/registration/edit_offerings.vis b/services/registration/edit_offerings.vis index dad3193..f95c2ae 100644 --- a/services/registration/edit_offerings.vis +++ b/services/registration/edit_offerings.vis @@ -2,9 +2,10 @@ CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_offerings flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info -LOAD save_offerings 0 +LOAD save_offerings 8 MOUT back 0 HALT RELOAD save_offerings INCMP _ 0 -INCMP pin_entry * +CATCH pin_entry flag_offerings_set 1 +INCMP update_profile_items * diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index af20e0f..dff221b 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -11,7 +11,7 @@ MOUT edit_offerings 6 MOUT view 7 MOUT back 0 HALT -INCMP my_account 0 +INCMP ^ 0 INCMP edit_first_name 1 INCMP edit_family_name 2 INCMP select_gender 3 diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis index 6a5abe0..d0fbcee 100644 --- a/services/registration/edit_yob.vis +++ b/services/registration/edit_yob.vis @@ -8,7 +8,9 @@ HALT LOAD verify_yob 6 RELOAD verify_yob CATCH incorrect_date_format flag_incorrect_date_format 1 -LOAD save_yob 0 +LOAD save_yob 32 RELOAD save_yob INCMP _ 0 -INCMP pin_entry * +CATCH pin_entry flag_yob_set 1 +INCMP edit_location * + diff --git a/services/registration/profile_update_success.vis b/services/registration/profile_update_success.vis index a035093..f670e6e 100644 --- a/services/registration/profile_update_success.vis +++ b/services/registration/profile_update_success.vis @@ -1,3 +1,5 @@ +LOAD update_all_profile_items 0 +RELOAD update_all_profile_items MOUT back 0 MOUT quit 9 HALT diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis index e211ada..d1d38bd 100644 --- a/services/registration/set_female.vis +++ b/services/registration/set_female.vis @@ -1,4 +1,5 @@ -LOAD save_gender 0 +LOAD save_gender 32 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 -MOVE pin_entry +CATCH pin_entry flag_gender_set 1 +MOVE edit_yob diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis index e211ada..e718dfc 100644 --- a/services/registration/set_male.vis +++ b/services/registration/set_male.vis @@ -1,4 +1,5 @@ -LOAD save_gender 0 +LOAD save_gender 16 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 -MOVE pin_entry +CATCH pin_entry flag_gender_set 1 +MOVE edit_yob diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis index e211ada..e5cfca8 100644 --- a/services/registration/set_unspecified.vis +++ b/services/registration/set_unspecified.vis @@ -1,4 +1,5 @@ -LOAD save_gender 0 +LOAD save_gender 8 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 -MOVE pin_entry +CATCH pin_entry flag_gender_set 1 +MOVE edit_yob diff --git a/services/registration/update_profile_items.vis b/services/registration/update_profile_items.vis new file mode 100644 index 0000000..beda013 --- /dev/null +++ b/services/registration/update_profile_items.vis @@ -0,0 +1,3 @@ +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH profile_update_success flag_allow_update 1 +MOVE pin_entry From 944fa89b3cffdf1ec4a19e0be842678d8d97e3bf Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 11:19:38 +0300 Subject: [PATCH 208/332] add profile holder struct --- models/profile.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 models/profile.go diff --git a/models/profile.go b/models/profile.go new file mode 100644 index 0000000..bdc1f0d --- /dev/null +++ b/models/profile.go @@ -0,0 +1,14 @@ +package models + +type Profile struct { + ProfileItems []string + Max int +} + +func (p *Profile) InsertOrShift(index int, value string) { + if index < len(p.ProfileItems) { + p.ProfileItems = append(p.ProfileItems[:index], value) + } else { + p.ProfileItems = append(p.ProfileItems, value) + } +} From a72fb08dc835ea6fb36279ade3a3a1fcafebbeb0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 11:20:35 +0300 Subject: [PATCH 209/332] allow sequential profile edit --- services/registration/edit_family_name.vis | 2 +- services/registration/edit_first_name.vis | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis index adcede1..df35970 100644 --- a/services/registration/edit_family_name.vis +++ b/services/registration/edit_family_name.vis @@ -3,8 +3,8 @@ LOAD get_current_profile_info 0 RELOAD get_current_profile_info MAP get_current_profile_info MOUT back 0 -LOAD save_familyname 64 HALT +LOAD save_familyname 64 RELOAD save_familyname INCMP _ 0 CATCH pin_entry flag_familyname_set 1 diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis index d230945..f6bfa26 100644 --- a/services/registration/edit_first_name.vis +++ b/services/registration/edit_first_name.vis @@ -2,10 +2,10 @@ CATCH update_firstname flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info MAP get_current_profile_info -LOAD save_firstname 128 MOUT back 0 HALT -RELOAD save_firstname INCMP _ 0 +LOAD save_firstname 128 +RELOAD save_firstname CATCH pin_entry flag_firstname_set 1 INCMP edit_family_name * From ff3f049226b45d395c1d6976909d299b3427aee9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 13:31:30 +0300 Subject: [PATCH 210/332] updated the CheckAliasAddress mock --- internal/testutil/mocks/servicemock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/testutil/mocks/servicemock.go b/internal/testutil/mocks/servicemock.go index 521cfa0..59d7205 100644 --- a/internal/testutil/mocks/servicemock.go +++ b/internal/testutil/mocks/servicemock.go @@ -49,6 +49,6 @@ func (m *MockAccountService) TokenTransfer(ctx context.Context, amount, from, to } func (m *MockAccountService) CheckAliasAddress(ctx context.Context, alias string) (*dataserviceapi.AliasAddress, error) { - args := m.Called() + args := m.Called(alias) return args.Get(0).(*dataserviceapi.AliasAddress), args.Error(1) } From e979742424f8b5526c7669a87f1ea8622d37110e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 13:32:18 +0300 Subject: [PATCH 211/332] resolved error in the TestValidateRecipient --- internal/handlers/ussd/menuhandler_test.go | 28 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6f1d57a..c2629c4 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1498,10 +1498,10 @@ func TestValidateRecipient(t *testing.T) { }{ { name: "Test with invalid recepient", - input: []byte("9234adf5"), + input: []byte("7?1234"), expectedResult: resource.Result{ FlagSet: []uint32{flag_invalid_recipient}, - Content: "9234adf5", + Content: "7?1234", }, }, { @@ -1517,22 +1517,40 @@ func TestValidateRecipient(t *testing.T) { input: []byte("0711223344"), expectedResult: resource.Result{}, }, + { + name: "Test with address", + input: []byte("0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9"), + expectedResult: resource.Result{}, + }, + { + name: "Test with alias recepient", + input: []byte("alias123"), + expectedResult: resource.Result{}, + }, } // store a public key for the valid recipient - err = store.WriteEntry(ctx, "0711223344", common.DATA_PUBLIC_KEY, []byte(publicKey)) + err = store.WriteEntry(ctx, "+254711223344", common.DATA_PUBLIC_KEY, []byte(publicKey)) if err != nil { t.Fatal(err) } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + mockAccountService := new(mocks.MockAccountService) // Create the Handlers instance h := &Handlers{ - flagManager: fm.parser, - userdataStore: store, + flagManager: fm.parser, + userdataStore: store, + accountService: mockAccountService, } + aliasResponse := &dataserviceapi.AliasAddress{ + Address: "0xd4c288865Ce0985a481Eef3be02443dF5E2e4Ea9", + } + + mockAccountService.On("CheckAliasAddress", string(tt.input)).Return(aliasResponse, nil) + // Call the method res, err := h.ValidateRecipient(ctx, "validate_recepient", tt.input) From 10586baf0d514692ffa3d3db0d5a7b6d23ef5b7c Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 13:35:14 +0300 Subject: [PATCH 212/332] resolved error in the TestCheckBalance --- internal/handlers/ussd/menuhandler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index c2629c4..6dfdcb8 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1582,7 +1582,7 @@ func TestCheckBalance(t *testing.T) { publicKey: "0X98765432109", activeSym: "ETH", activeBal: "1.5", - expectedResult: resource.Result{Content: "Balance: 1.5 ETH\n"}, + expectedResult: resource.Result{Content: "Balance: 1.50 ETH\n"}, expectError: false, }, } From 0f21b0181358dc718ed619bd9d3505a32690d627 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 13:37:00 +0300 Subject: [PATCH 213/332] resolved error in the TestViewVoucher --- internal/handlers/ussd/menuhandler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 6dfdcb8..d7cd855 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -2008,7 +2008,7 @@ func TestViewVoucher(t *testing.T) { res, err := h.ViewVoucher(ctx, "view_voucher", []byte("1")) assert.NoError(t, err) - assert.Equal(t, res.Content, "SRF\n100") + assert.Equal(t, res.Content, "Symbol: SRF\nBalance: 100") } func TestSetVoucher(t *testing.T) { From ba430a5849030c3fd4581c6596c8947ecff160e0 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 14:10:05 +0300 Subject: [PATCH 214/332] add a separate function to handle ConstructName --- internal/handlers/ussd/menuhandler.go | 13 +------------ internal/utils/name.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 internal/utils/name.go diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 6bc00ec..acd3dd4 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1398,18 +1398,7 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) offerings := getEntryOrDefault(store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS)) // Construct the full name - name := defaultValue - if familyName != defaultValue { - if firstName != defaultValue { - name = firstName + " " + familyName - } else { - name = familyName - } - } else { - if firstName != defaultValue { - name = firstName - } - } + name := utils.ConstructName(firstName, familyName, defaultValue) // Calculate age from year of birth age := defaultValue diff --git a/internal/utils/name.go b/internal/utils/name.go new file mode 100644 index 0000000..4665d02 --- /dev/null +++ b/internal/utils/name.go @@ -0,0 +1,17 @@ +package utils + +func ConstructName(firstName, familyName, defaultValue string) string { + name := defaultValue + if familyName != defaultValue { + if firstName != defaultValue { + name = firstName + " " + familyName + } else { + name = familyName + } + } else { + if firstName != defaultValue { + name = firstName + } + } + return name +} \ No newline at end of file From d40a4a171ff1bb84b7cc299e13f3b04836cdb9bd Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Dec 2024 14:12:47 +0300 Subject: [PATCH 215/332] formatted code --- internal/utils/name.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/utils/name.go b/internal/utils/name.go index 4665d02..ea403d5 100644 --- a/internal/utils/name.go +++ b/internal/utils/name.go @@ -1,17 +1,17 @@ package utils func ConstructName(firstName, familyName, defaultValue string) string { - name := defaultValue - if familyName != defaultValue { - if firstName != defaultValue { - name = firstName + " " + familyName - } else { - name = familyName - } - } else { - if firstName != defaultValue { - name = firstName - } - } - return name -} \ No newline at end of file + name := defaultValue + if familyName != defaultValue { + if firstName != defaultValue { + name = firstName + " " + familyName + } else { + name = familyName + } + } else { + if firstName != defaultValue { + name = firstName + } + } + return name +} From 1c7c0af712851166a13e3a1f1e4fc98ea8612f1b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 14:36:48 +0300 Subject: [PATCH 216/332] catch next unset profile item --- services/registration/edit_family_name.vis | 5 +++++ services/registration/edit_first_name.vis | 6 ++++++ services/registration/edit_location.vis | 2 ++ services/registration/edit_offerings.vis | 1 + services/registration/edit_yob.vis | 4 +++- services/registration/set_female.vis | 4 ++++ services/registration/set_male.vis | 4 ++++ services/registration/set_unspecified.vis | 4 ++++ 8 files changed, 29 insertions(+), 1 deletion(-) diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis index df35970..bc3c308 100644 --- a/services/registration/edit_family_name.vis +++ b/services/registration/edit_family_name.vis @@ -8,4 +8,9 @@ LOAD save_familyname 64 RELOAD save_familyname INCMP _ 0 CATCH pin_entry flag_familyname_set 1 +CATCH select_gender flag_gender_set 0 +CATCH edit_yob flag_yob_set 0 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_familyname_set 0 INCMP select_gender * diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis index f6bfa26..9940af3 100644 --- a/services/registration/edit_first_name.vis +++ b/services/registration/edit_first_name.vis @@ -8,4 +8,10 @@ INCMP _ 0 LOAD save_firstname 128 RELOAD save_firstname CATCH pin_entry flag_firstname_set 1 +CATCH edit_family_name flag_familyname_set 0 +CATCH edit_gender flag_gender_set 0 +CATCH edit_yob flag_yob_set 0 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_firstname_set 0 INCMP edit_family_name * diff --git a/services/registration/edit_location.vis b/services/registration/edit_location.vis index c9bc12d..524df24 100644 --- a/services/registration/edit_location.vis +++ b/services/registration/edit_location.vis @@ -8,4 +8,6 @@ HALT RELOAD save_location INCMP _ 0 CATCH pin_entry flag_location_set 1 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_location_set 0 INCMP edit_offerings * diff --git a/services/registration/edit_offerings.vis b/services/registration/edit_offerings.vis index f95c2ae..65e997e 100644 --- a/services/registration/edit_offerings.vis +++ b/services/registration/edit_offerings.vis @@ -8,4 +8,5 @@ HALT RELOAD save_offerings INCMP _ 0 CATCH pin_entry flag_offerings_set 1 +CATCH pin_entry flag_offerings_set 0 INCMP update_profile_items * diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis index d0fbcee..787f4ea 100644 --- a/services/registration/edit_yob.vis +++ b/services/registration/edit_yob.vis @@ -12,5 +12,7 @@ LOAD save_yob 32 RELOAD save_yob INCMP _ 0 CATCH pin_entry flag_yob_set 1 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_yob_set 0 INCMP edit_location * - diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis index d1d38bd..5221702 100644 --- a/services/registration/set_female.vis +++ b/services/registration/set_female.vis @@ -2,4 +2,8 @@ LOAD save_gender 32 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 +CATCH edit_yob flag_yob_set 0 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_gender_set 0 MOVE edit_yob diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis index e718dfc..c7d20d1 100644 --- a/services/registration/set_male.vis +++ b/services/registration/set_male.vis @@ -2,4 +2,8 @@ LOAD save_gender 16 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 +CATCH edit_yob flag_yob_set 0 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_gender_set 0 MOVE edit_yob diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis index e5cfca8..0350500 100644 --- a/services/registration/set_unspecified.vis +++ b/services/registration/set_unspecified.vis @@ -2,4 +2,8 @@ LOAD save_gender 8 CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 +CATCH edit_yob flag_yob_set 0 +CATCH edit_location flag_location_set 0 +CATCH edit_offerings flag_offerings_set 0 +CATCH pin_entry flag_gender_set 0 MOVE edit_yob From 9e998f9a29bed4c4a7b88c629ad605606b01725a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 14:37:55 +0300 Subject: [PATCH 217/332] add a zero pad value to unfilled profile item --- models/profile.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/models/profile.go b/models/profile.go index bdc1f0d..d698318 100644 --- a/models/profile.go +++ b/models/profile.go @@ -9,6 +9,10 @@ func (p *Profile) InsertOrShift(index int, value string) { if index < len(p.ProfileItems) { p.ProfileItems = append(p.ProfileItems[:index], value) } else { - p.ProfileItems = append(p.ProfileItems, value) + for len(p.ProfileItems) < index { + p.ProfileItems = append(p.ProfileItems, "0") + } + p.ProfileItems = append(p.ProfileItems, "0") + p.ProfileItems[index] = value } } From e0ec15b27273cde8da9510c055d2e6d40bf8a87a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 14:40:57 +0300 Subject: [PATCH 218/332] allow sequential profile edit --- internal/handlers/ussd/menuhandler.go | 189 ++++++++++++++++++++++---- 1 file changed, 165 insertions(+), 24 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8d3b928..cddb473 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -21,6 +21,7 @@ import ( "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/internal/utils" + "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" @@ -78,6 +79,7 @@ type Handlers struct { flagManager *asm.FlagParser accountService remote.AccountServiceInterface prefixDb storage.PrefixDb + profile *models.Profile } func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface) (*Handlers, error) { @@ -96,6 +98,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util adminstore: adminstore, accountService: accountService, prefixDb: prefixDb, + profile: &models.Profile{Max: 6}, } return h, nil } @@ -406,7 +409,10 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) firstName := string(input) store := h.userdataStore flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_firstname_set, _ := h.flagManager.GetFlag("flag_firstname_set") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + firstNameSet := h.st.MatchFlag(flag_firstname_set, true) if allowUpdate { temporaryFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(temporaryFirstName)) @@ -414,11 +420,16 @@ func (h *Handlers) SaveFirstname(ctx context.Context, sym string, input []byte) logg.ErrorCtxf(ctx, "failed to write firstName entry with", "key", common.DATA_FIRST_NAME, "value", temporaryFirstName, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_firstname_set) } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryFirstName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) - return res, err + if firstNameSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(firstName)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryFirstName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", firstName, "error", err) + return res, err + } + } else { + h.profile.InsertOrShift(0, firstName) } } @@ -438,7 +449,9 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) familyName := string(input) flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_familyname_set, _ := h.flagManager.GetFlag("flag_familyname_set") allowUpdate := h.st.MatchFlag(flag_allow_update, true) + familyNameSet := h.st.MatchFlag(flag_familyname_set, true) if allowUpdate { temporaryFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -447,11 +460,16 @@ func (h *Handlers) SaveFamilyname(ctx context.Context, sym string, input []byte) logg.ErrorCtxf(ctx, "failed to write familyName entry with", "key", common.DATA_FAMILY_NAME, "value", temporaryFamilyName, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_familyname_set) } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryFamilyName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", familyName, "error", err) - return res, err + if familyNameSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(familyName)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryFamilyName entry with", "key", common.DATA_TEMPORARY_VALUE, "value", familyName, "error", err) + return res, err + } + } else { + h.profile.InsertOrShift(1, familyName) } } @@ -469,7 +487,10 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou yob := string(input) store := h.userdataStore flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_yob_set, _ := h.flagManager.GetFlag("flag_yob_set") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + yobSet := h.st.MatchFlag(flag_yob_set, true) if allowUpdate { temporaryYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -478,11 +499,16 @@ func (h *Handlers) SaveYob(ctx context.Context, sym string, input []byte) (resou logg.ErrorCtxf(ctx, "failed to write yob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryYob, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_yob_set) } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryYob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) - return res, err + if yobSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(yob)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryYob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", yob, "error", err) + return res, err + } + } else { + h.profile.InsertOrShift(3, yob) } } @@ -501,7 +527,9 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( store := h.userdataStore flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_location_set, _ := h.flagManager.GetFlag("flag_location_set") allowUpdate := h.st.MatchFlag(flag_allow_update, true) + locationSet := h.st.MatchFlag(flag_location_set, true) if allowUpdate { temporaryLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -511,10 +539,15 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( return res, err } } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryLocation entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) - return res, err + if locationSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryLocation entry with", "key", common.DATA_TEMPORARY_VALUE, "value", location, "error", err) + return res, err + } + res.FlagSet = append(res.FlagSet, flag_location_set) + } else { + h.profile.InsertOrShift(4, location) } } @@ -533,7 +566,11 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re gender := strings.Split(symbol, "_")[1] store := h.userdataStore flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_gender_set, _ := h.flagManager.GetFlag("flag_gender_set") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + genderSet := h.st.MatchFlag(flag_gender_set, true) + fmt.Println("GenderSet:", genderSet) if allowUpdate { temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -542,11 +579,16 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re logg.ErrorCtxf(ctx, "failed to write gender entry with", "key", common.DATA_GENDER, "value", gender, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_gender_set) } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryGender entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) - return res, err + if genderSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(gender)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryGender entry with", "key", common.DATA_TEMPORARY_VALUE, "value", gender, "error", err) + return res, err + } + } else { + h.profile.InsertOrShift(2, gender) } } @@ -566,7 +608,10 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) store := h.userdataStore flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_offerings_set, _ := h.flagManager.GetFlag("flag_offerings_set") + allowUpdate := h.st.MatchFlag(flag_allow_update, true) + offeringsSet := h.st.MatchFlag(flag_offerings_set, true) if allowUpdate { temporaryOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -575,11 +620,16 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) logg.ErrorCtxf(ctx, "failed to write offerings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_offerings_set) } else { - err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryOfferings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) - return res, err + if offeringsSet { + err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(offerings)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write temporaryOfferings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", offerings, "error", err) + return res, err + } + } else { + h.profile.InsertOrShift(5, offerings) } } @@ -1834,3 +1884,94 @@ func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, inp return res, nil } + +func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res *resource.Result) error { + var err error + store := h.userdataStore + profileFlagNames := []string{ + "flag_firstname_set", + "flag_familyname_set", + "flag_yob_set", + "flag_gender_set", + "flag_location_set", + "flag_offerings_set", + } + for index, profileItem := range h.profile.ProfileItems { + switch index { + case 0: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile first name entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + case 1: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile family name entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + case 2: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile gender entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + case 3: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile yob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + case 4: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile location entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + case 5: + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile offerings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) + return err + } + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) + } + } + } + return nil +} + +func (h *Handlers) UpdateAllProfileItems(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + sessionId, ok := ctx.Value("SessionId").(string) + if !ok { + return res, fmt.Errorf("missing session") + } + err := h.insertProfileItems(ctx, sessionId, &res) + if err != nil { + return res, err + } + return res, nil +} From a20ab793554509ab0bcc9292e140b87008f4e994 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 17:38:20 +0300 Subject: [PATCH 219/332] explicit reload save gender --- services/registration/set_female.vis | 1 + services/registration/set_male.vis | 1 + services/registration/set_unspecified.vis | 1 + 3 files changed, 3 insertions(+) diff --git a/services/registration/set_female.vis b/services/registration/set_female.vis index 5221702..da92520 100644 --- a/services/registration/set_female.vis +++ b/services/registration/set_female.vis @@ -1,4 +1,5 @@ LOAD save_gender 32 +RELOAD save_gender CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 diff --git a/services/registration/set_male.vis b/services/registration/set_male.vis index c7d20d1..9a95937 100644 --- a/services/registration/set_male.vis +++ b/services/registration/set_male.vis @@ -1,4 +1,5 @@ LOAD save_gender 16 +RELOAD save_gender CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 diff --git a/services/registration/set_unspecified.vis b/services/registration/set_unspecified.vis index 0350500..824105c 100644 --- a/services/registration/set_unspecified.vis +++ b/services/registration/set_unspecified.vis @@ -1,4 +1,5 @@ LOAD save_gender 8 +RELOAD save_gender CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_gender flag_allow_update 1 CATCH pin_entry flag_gender_set 1 From b420a9bba0dcbe210946e12daa6bbeeb607fe9a6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 17:41:06 +0300 Subject: [PATCH 220/332] set flag if profile data is set --- internal/handlers/ussd/menuhandler.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index cddb473..184e045 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -570,7 +570,6 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re allowUpdate := h.st.MatchFlag(flag_allow_update, true) genderSet := h.st.MatchFlag(flag_gender_set, true) - fmt.Println("GenderSet:", genderSet) if allowUpdate { temporaryGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE) @@ -1312,6 +1311,14 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input var res resource.Result var profileInfo []byte var err error + + flag_firstname_set, _ := h.flagManager.GetFlag("flag_firstname_set") + flag_familyname_set, _ := h.flagManager.GetFlag("flag_familyname_set") + flag_yob_set, _ := h.flagManager.GetFlag("flag_yob_set") + flag_gender_set, _ := h.flagManager.GetFlag("flag_gender_set") + flag_location_set, _ := h.flagManager.GetFlag("flag_location_set") + flag_offerings_set, _ := h.flagManager.GetFlag("flag_offerings_set") + sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") @@ -1338,6 +1345,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read first name entry with", "key", "error", common.DATA_FIRST_NAME, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_firstname_set) res.Content = string(profileInfo) case common.DATA_FAMILY_NAME: profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) @@ -1349,6 +1357,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read family name entry with", "key", "error", common.DATA_FAMILY_NAME, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_familyname_set) res.Content = string(profileInfo) case common.DATA_GENDER: @@ -1361,6 +1370,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read gender entry with", "key", "error", common.DATA_GENDER, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_gender_set) res.Content = string(profileInfo) case common.DATA_YOB: profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_YOB) @@ -1372,8 +1382,8 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read year of birth(yob) entry with", "key", "error", common.DATA_YOB, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_yob_set) res.Content = string(profileInfo) - case common.DATA_LOCATION: profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) if err != nil { @@ -1384,6 +1394,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read location entry with", "key", "error", common.DATA_LOCATION, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_location_set) res.Content = string(profileInfo) case common.DATA_OFFERINGS: profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) @@ -1395,6 +1406,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input logg.ErrorCtxf(ctx, "Failed to read offerings entry with", "key", "error", common.DATA_OFFERINGS, err) return res, err } + res.FlagSet = append(res.FlagSet, flag_offerings_set) res.Content = string(profileInfo) default: break From d516584d905292df237a153d41de8182c6c599ce Mon Sep 17 00:00:00 2001 From: lash Date: Tue, 3 Dec 2024 16:16:46 +0000 Subject: [PATCH 221/332] Clear persister from handler in outer code aswell --- internal/handlers/base.go | 3 +++ internal/handlers/ussd/menuhandler.go | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/handlers/base.go b/internal/handlers/base.go index 4d2aa4c..755cca4 100644 --- a/internal/handlers/base.go +++ b/internal/handlers/base.go @@ -55,6 +55,9 @@ func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error) } f.hn = f.hn.WithPersister(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 { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a79d054..5c1caff 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -113,7 +113,7 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource return r, nil } defer func() { - h.pe = nil + h.Exit() }() h.st = h.pe.GetState() @@ -140,6 +140,10 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource return r, nil } +func (h *Handlers) Exit() { + h.pe = nil +} + // SetLanguage sets the language across the menu func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result From c0ed6fa9c87c4283b8f9c818931cb1b3761b5403 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Dec 2024 21:43:24 +0300 Subject: [PATCH 222/332] catch incorrect pin entries --- services/registration/edit_family_name.vis | 1 + services/registration/edit_first_name.vis | 1 + 2 files changed, 2 insertions(+) diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis index bc3c308..31249ce 100644 --- a/services/registration/edit_family_name.vis +++ b/services/registration/edit_family_name.vis @@ -1,3 +1,4 @@ +CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_familyname flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis index 9940af3..ad06c92 100644 --- a/services/registration/edit_first_name.vis +++ b/services/registration/edit_first_name.vis @@ -1,3 +1,4 @@ +CATCH incorrect_pin flag_incorrect_pin 1 CATCH update_firstname flag_allow_update 1 LOAD get_current_profile_info 0 RELOAD get_current_profile_info From c4282a870e30e6c31261f6f784da8375bded47a7 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 08:19:20 +0300 Subject: [PATCH 223/332] add flag to catch back navigations --- services/registration/pp.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 6c74b95..26a8833 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -27,3 +27,4 @@ flag,flag_yob_set,33,this is set when the yob of the profile is set flag,flag_gender_set,34,this is set when the gender of the profile is set flag,flag_location_set,35,this is set when the location of the profile is set flag,flag_offerings_set,36,this is set when the offerings of the profile is set +flag,flag_back_set,37,this is set when it is a back navigation From c5ebdbf85b7f2c7d536d9b7b1849160663915c23 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 08:20:09 +0300 Subject: [PATCH 224/332] catch back navigations --- services/registration/edit_family_name.vis | 3 ++- services/registration/edit_first_name.vis | 4 ++-- services/registration/edit_location.vis | 2 ++ services/registration/edit_offerings.vis | 2 ++ services/registration/edit_profile.vis | 1 + services/registration/edit_yob.vis | 3 ++- 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/services/registration/edit_family_name.vis b/services/registration/edit_family_name.vis index 31249ce..590eab1 100644 --- a/services/registration/edit_family_name.vis +++ b/services/registration/edit_family_name.vis @@ -5,9 +5,10 @@ RELOAD get_current_profile_info MAP get_current_profile_info MOUT back 0 HALT +RELOAD set_back +CATCH _ flag_back_set 1 LOAD save_familyname 64 RELOAD save_familyname -INCMP _ 0 CATCH pin_entry flag_familyname_set 1 CATCH select_gender flag_gender_set 0 CATCH edit_yob flag_yob_set 0 diff --git a/services/registration/edit_first_name.vis b/services/registration/edit_first_name.vis index ad06c92..6848b9c 100644 --- a/services/registration/edit_first_name.vis +++ b/services/registration/edit_first_name.vis @@ -5,7 +5,8 @@ RELOAD get_current_profile_info MAP get_current_profile_info MOUT back 0 HALT -INCMP _ 0 +RELOAD set_back +CATCH _ flag_back_set 1 LOAD save_firstname 128 RELOAD save_firstname CATCH pin_entry flag_firstname_set 1 @@ -15,4 +16,3 @@ CATCH edit_yob flag_yob_set 0 CATCH edit_location flag_location_set 0 CATCH edit_offerings flag_offerings_set 0 CATCH pin_entry flag_firstname_set 0 -INCMP edit_family_name * diff --git a/services/registration/edit_location.vis b/services/registration/edit_location.vis index 524df24..e4fcd8b 100644 --- a/services/registration/edit_location.vis +++ b/services/registration/edit_location.vis @@ -5,6 +5,8 @@ RELOAD get_current_profile_info LOAD save_location 16 MOUT back 0 HALT +RELOAD set_back +CATCH _ flag_back_set 1 RELOAD save_location INCMP _ 0 CATCH pin_entry flag_location_set 1 diff --git a/services/registration/edit_offerings.vis b/services/registration/edit_offerings.vis index 65e997e..ddbc9e0 100644 --- a/services/registration/edit_offerings.vis +++ b/services/registration/edit_offerings.vis @@ -5,6 +5,8 @@ RELOAD get_current_profile_info LOAD save_offerings 8 MOUT back 0 HALT +RELOAD set_back +CATCH _ flag_back_set 1 RELOAD save_offerings INCMP _ 0 CATCH pin_entry flag_offerings_set 1 diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index dff221b..e5ee12b 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -11,6 +11,7 @@ MOUT edit_offerings 6 MOUT view 7 MOUT back 0 HALT +LOAD set_back 6 INCMP ^ 0 INCMP edit_first_name 1 INCMP edit_family_name 2 diff --git a/services/registration/edit_yob.vis b/services/registration/edit_yob.vis index 787f4ea..255bea5 100644 --- a/services/registration/edit_yob.vis +++ b/services/registration/edit_yob.vis @@ -5,12 +5,13 @@ RELOAD get_current_profile_info MAP get_current_profile_info MOUT back 0 HALT +RELOAD set_back +CATCH _ flag_back_set 1 LOAD verify_yob 6 RELOAD verify_yob CATCH incorrect_date_format flag_incorrect_date_format 1 LOAD save_yob 32 RELOAD save_yob -INCMP _ 0 CATCH pin_entry flag_yob_set 1 CATCH edit_location flag_location_set 0 CATCH edit_offerings flag_offerings_set 0 From 5c202741d6270c36f561ebc8fc49c8662206200b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 08:20:43 +0300 Subject: [PATCH 225/332] add handler for catching back navigations --- internal/handlers/handlerservice.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 13ca70a..a14cf59 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -122,6 +122,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("get_transactions", ussdHandlers.GetTransactionsList) ls.DbRs.AddLocalFunc("view_statement", ussdHandlers.ViewTransactionStatement) ls.DbRs.AddLocalFunc("update_all_profile_items", ussdHandlers.UpdateAllProfileItems) + ls.DbRs.AddLocalFunc("set_back", ussdHandlers.SetBack) return ussdHandlers, nil } From ed632248c5369d0a1bf6084f4620bab18f9195c6 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 08:30:07 +0300 Subject: [PATCH 226/332] add doc lines and check for back naviagtions --- internal/handlers/ussd/menuhandler.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index bd7ee7e..8fc6175 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -726,6 +726,18 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by return res, nil } +// Setback sets the flag_back_set flag when the navigation is back +func (h *Handlers) SetBack(ctx context.Context, sym string, input []byte) (resource.Result, error) { + var res resource.Result + //TODO: + //Add check if the navigation is lateral nav instead of checking the input. + if string(input) == "0" { + flag_back_set, _ := h.flagManager.GetFlag("flag_back_set") + res.FlagSet = append(res.FlagSet, flag_back_set) + } + return res, nil +} + // CheckAccountStatus queries the API using the TrackingId and sets flags // based on the account status func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { @@ -1332,6 +1344,9 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input flag_gender_set, _ := h.flagManager.GetFlag("flag_gender_set") flag_location_set, _ := h.flagManager.GetFlag("flag_location_set") flag_offerings_set, _ := h.flagManager.GetFlag("flag_offerings_set") + flag_back_set, _ := h.flagManager.GetFlag("flag_back_set") + + res.FlagReset = append(res.FlagReset, flag_back_set) sessionId, ok := ctx.Value("SessionId").(string) if !ok { @@ -2017,6 +2032,7 @@ func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res return nil } +// UpdateAllProfileItems is used to persist all the new profile information and setup the required profile flags func (h *Handlers) UpdateAllProfileItems(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From e7c4b5bca73cc38241869e69a8b7913cb7de44fb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 09:08:25 +0300 Subject: [PATCH 227/332] update tests --- internal/handlers/ussd/menuhandler_test.go | 54 +++++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index d7cd855..a1e4f3d 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -179,11 +179,14 @@ func TestSaveFirstname(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_firstname_set, _ := fm.GetFlag("flag_firstname_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(128) mockState.SetFlag(flag_allow_update) + expectedResult := resource.Result{} + // Define test data firstName := "John" @@ -191,6 +194,8 @@ func TestSaveFirstname(t *testing.T) { t.Fatal(err) } + expectedResult.FlagSet = []uint32{flag_firstname_set} + // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: store, @@ -203,7 +208,7 @@ func TestSaveFirstname(t *testing.T) { // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_FIRST_NAME entry has been updated with the temporary value storedFirstName, _ := store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME) @@ -218,11 +223,16 @@ func TestSaveFamilyname(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_firstname_set, _ := fm.GetFlag("flag_familyname_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(128) mockState.SetFlag(flag_allow_update) + expectedResult := resource.Result{} + + expectedResult.FlagSet = []uint32{flag_firstname_set} + // Define test data familyName := "Doeee" @@ -242,7 +252,7 @@ func TestSaveFamilyname(t *testing.T) { // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_FAMILY_NAME entry has been updated with the temporary value storedFamilyName, _ := store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) @@ -257,11 +267,14 @@ func TestSaveYoB(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_yob_set, _ := fm.GetFlag("flag_yob_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(108) mockState.SetFlag(flag_allow_update) + expectedResult := resource.Result{} + // Define test data yob := "1980" @@ -269,6 +282,8 @@ func TestSaveYoB(t *testing.T) { t.Fatal(err) } + expectedResult.FlagSet = []uint32{flag_yob_set} + // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: store, @@ -281,7 +296,7 @@ func TestSaveYoB(t *testing.T) { // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_YOB entry has been updated with the temporary value storedYob, _ := store.ReadEntry(ctx, sessionId, common.DATA_YOB) @@ -296,11 +311,14 @@ func TestSaveLocation(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_location_set, _ := fm.GetFlag("flag_location_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(108) mockState.SetFlag(flag_allow_update) + expectedResult := resource.Result{} + // Define test data location := "Kilifi" @@ -308,6 +326,8 @@ func TestSaveLocation(t *testing.T) { t.Fatal(err) } + expectedResult.FlagSet = []uint32{flag_location_set} + // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: store, @@ -320,7 +340,7 @@ func TestSaveLocation(t *testing.T) { // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_LOCATION entry has been updated with the temporary value storedLocation, _ := store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) @@ -335,11 +355,14 @@ func TestSaveOfferings(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_offerings_set, _ := fm.GetFlag("flag_offerings_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(108) mockState.SetFlag(flag_allow_update) + expectedResult := resource.Result{} + // Define test data offerings := "Bananas" @@ -347,6 +370,8 @@ func TestSaveOfferings(t *testing.T) { t.Fatal(err) } + expectedResult.FlagSet = []uint32{flag_offerings_set} + // Create the Handlers instance with the mock store h := &Handlers{ userdataStore: store, @@ -359,7 +384,7 @@ func TestSaveOfferings(t *testing.T) { // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_OFFERINGS entry has been updated with the temporary value storedOfferings, _ := store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) @@ -374,9 +399,10 @@ func TestSaveGender(t *testing.T) { fm, _ := NewFlagManager(flagsPath) flag_allow_update, _ := fm.GetFlag("flag_allow_update") + flag_gender_set, _ := fm.GetFlag("flag_gender_set") // Set the flag in the State - mockState := state.NewState(16) + mockState := state.NewState(108) mockState.SetFlag(flag_allow_update) // Define test cases @@ -420,12 +446,16 @@ func TestSaveGender(t *testing.T) { flagManager: fm.parser, } + expectedResult := resource.Result{} + // Call the method res, err := h.SaveGender(ctx, "save_gender", tt.input) + expectedResult.FlagSet = []uint32{flag_gender_set} + // Assert results assert.NoError(t, err) - assert.Equal(t, resource.Result{}, res) + assert.Equal(t, expectedResult, res) // Verify that the DATA_GENDER entry has been updated with the temporary value storedGender, _ := store.ReadEntry(ctx, sessionId, common.DATA_GENDER) From 8efed966a053fa8b1cd32af9806f550653b1ae12 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 09:08:47 +0300 Subject: [PATCH 228/332] set flag when location is set --- internal/handlers/ussd/menuhandler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8fc6175..19f9f97 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -543,6 +543,7 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) ( logg.ErrorCtxf(ctx, "failed to write location entry with", "key", common.DATA_LOCATION, "value", temporaryLocation, "error", err) return res, err } + res.FlagSet = append(res.FlagSet, flag_location_set) } else { if locationSet { err = store.WriteEntry(ctx, sessionId, common.DATA_TEMPORARY_VALUE, []byte(location)) From a84c3e0852d35861776a28889c567041a6c56d8d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Wed, 4 Dec 2024 09:46:16 +0300 Subject: [PATCH 229/332] update menu traversal test data --- menutraversal_test/group_test.json | 50 +++++++++++++----------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 7985b22..c382816 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -187,6 +187,26 @@ }, { "input": "foo", + "expectedContent": "Enter family name:\n0:Back" + }, + { + "input": "bar", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", "expectedContent": "Please enter your PIN:" }, { @@ -197,10 +217,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -238,10 +254,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -280,10 +292,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -321,10 +329,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -362,10 +366,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -403,10 +403,6 @@ "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" @@ -434,16 +430,12 @@ }, { "input": "1234", - "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 84\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" }, { "input": "0", "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" }, - { - "input": "0", - "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" - }, { "input": "0", "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" From 4ecfc9de38ac602a632eaab4b95010ea1f72d47d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Dec 2024 20:38:52 +0300 Subject: [PATCH 230/332] removed DATATYPE_USERSUB and replaced with DATATYPE_USERDATA --- internal/storage/db.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/storage/db.go b/internal/storage/db.go index 8c9ff35..457af78 100644 --- a/internal/storage/db.go +++ b/internal/storage/db.go @@ -6,10 +6,6 @@ import ( "git.defalsify.org/vise.git/db" ) -const ( - DATATYPE_USERSUB = 64 -) - // PrefixDb interface abstracts the database operations. type PrefixDb interface { Get(ctx context.Context, key []byte) ([]byte, error) @@ -35,13 +31,13 @@ func (s *SubPrefixDb) toKey(k []byte) []byte { } func (s *SubPrefixDb) Get(ctx context.Context, key []byte) ([]byte, error) { - s.store.SetPrefix(DATATYPE_USERSUB) + s.store.SetPrefix(db.DATATYPE_USERDATA) key = s.toKey(key) return s.store.Get(ctx, key) } func (s *SubPrefixDb) Put(ctx context.Context, key []byte, val []byte) error { - s.store.SetPrefix(DATATYPE_USERSUB) + s.store.SetPrefix(db.DATATYPE_USERDATA) key = s.toKey(key) return s.store.Put(ctx, key, val) } From 93c44861e04c426be2069bc8993b4b2d4b084692 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Dec 2024 20:42:47 +0300 Subject: [PATCH 231/332] Use numeric prefixes --- common/db.go | 20 ++++++++++++- common/transfer_statements.go | 20 ++++++------- common/vouchers.go | 18 +++++++----- internal/handlers/ussd/menuhandler.go | 42 +++++++++++++-------------- 4 files changed, 60 insertions(+), 40 deletions(-) diff --git a/common/db.go b/common/db.go index e90fd47..1454a84 100644 --- a/common/db.go +++ b/common/db.go @@ -32,7 +32,18 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS - DATA_TRANSACTIONS + DATA_PREFIX_SYMBOLS + DATA_PREFIX_BALANCES + DATA_PREFIX_DECIMALS + DATA_PREFIX_ADDRESSES + DATA_PREFIX_TX_SENDERS + DATA_PREFIX_TX_RECIPIENTS + DATA_PREFIX_TX_VALUES + DATA_PREFIX_TX_ADDRESSES + DATA_PREFIX_TX_HASHES + DATA_PREFIX_TX_DATES + DATA_PREFIX_TX_SYMBOLS + DATA_PREFIX_TX_DECIMALS ) var ( @@ -69,3 +80,10 @@ func StringToDataTyp(str string) (DataTyp, error) { return 0, errors.New("invalid DataTyp string") } } + +// Convert DataTyp to []byte +func (d DataTyp) ToBytes() []byte { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, uint16(d)) + return bytes +} diff --git a/common/transfer_statements.go b/common/transfer_statements.go index 4dd409c..adbdeaf 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -57,11 +57,11 @@ func ProcessTransfers(transfers []dataserviceapi.Last10TxResponse) TransferMetad // GetTransferData retrieves and matches transfer data // returns a formatted string of the full transaction/statement func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, index int) (string, error) { - keys := []string{"txfrom", "txto", "txval", "txaddr", "txhash", "txdate", "txsym"} - data := make(map[string]string) + keys := []DataTyp{DATA_PREFIX_TX_SENDERS, DATA_PREFIX_TX_RECIPIENTS, DATA_PREFIX_TX_VALUES, DATA_PREFIX_TX_ADDRESSES, DATA_PREFIX_TX_HASHES, DATA_PREFIX_TX_DATES, DATA_PREFIX_TX_SYMBOLS} + data := make(map[DataTyp]string) for _, key := range keys { - value, err := db.Get(ctx, []byte(key)) + value, err := db.Get(ctx, key.ToBytes()) if err != nil { return "", fmt.Errorf("failed to get %s: %v", key, err) } @@ -69,13 +69,13 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, } // Split the data - senders := strings.Split(string(data["txfrom"]), "\n") - recipients := strings.Split(string(data["txto"]), "\n") - values := strings.Split(string(data["txval"]), "\n") - addresses := strings.Split(string(data["txaddr"]), "\n") - hashes := strings.Split(string(data["txhash"]), "\n") - dates := strings.Split(string(data["txdate"]), "\n") - syms := strings.Split(string(data["txsym"]), "\n") + senders := strings.Split(string(data[DATA_PREFIX_TX_SENDERS]), "\n") + recipients := strings.Split(string(data[DATA_PREFIX_TX_RECIPIENTS]), "\n") + values := strings.Split(string(data[DATA_PREFIX_TX_VALUES]), "\n") + addresses := strings.Split(string(data[DATA_PREFIX_TX_ADDRESSES]), "\n") + hashes := strings.Split(string(data[DATA_PREFIX_TX_HASHES]), "\n") + dates := strings.Split(string(data[DATA_PREFIX_TX_DATES]), "\n") + syms := strings.Split(string(data[DATA_PREFIX_TX_SYMBOLS]), "\n") // Check if index is within range if index < 1 || index > len(senders) { diff --git a/common/vouchers.go b/common/vouchers.go index c542084..5863c46 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -64,22 +64,24 @@ func ScaleDownBalance(balance, decimals string) string { // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { - keys := []string{"sym", "bal", "deci", "addr"} - data := make(map[string]string) + keys := []DataTyp{DATA_PREFIX_SYMBOLS, DATA_PREFIX_BALANCES, DATA_PREFIX_DECIMALS, DATA_PREFIX_ADDRESSES} + data := make(map[DataTyp]string) for _, key := range keys { - value, err := db.Get(ctx, []byte(key)) + value, err := db.Get(ctx, key.ToBytes()) if err != nil { - return nil, fmt.Errorf("failed to get %s: %v", key, err) + fmt.Printf("failed to get %v: %v\n", key, err) + continue } data[key] = string(value) } symbol, balance, decimal, address := MatchVoucher(input, - data["sym"], - data["bal"], - data["deci"], - data["addr"]) + data[DATA_PREFIX_SYMBOLS], + data[DATA_PREFIX_BALANCES], + data[DATA_PREFIX_DECIMALS], + data[DATA_PREFIX_ADDRESSES], + ) if symbol == "" { return nil, nil diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a79d054..2d5691f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1569,15 +1569,15 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) data := common.ProcessVouchers(vouchersResp) // Store all voucher data - dataMap := map[string]string{ - "sym": data.Symbols, - "bal": data.Balances, - "deci": data.Decimals, - "addr": data.Addresses, + dataMap := map[common.DataTyp]string{ + common.DATA_PREFIX_SYMBOLS: data.Symbols, + common.DATA_PREFIX_BALANCES: data.Balances, + common.DATA_PREFIX_DECIMALS: data.Decimals, + common.DATA_PREFIX_ADDRESSES: data.Addresses, } for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil { + if err := h.prefixDb.Put(ctx, []byte(key.ToBytes()), []byte(value)); err != nil { return res, nil } } @@ -1590,7 +1590,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) var res resource.Result // Read vouchers from the store - voucherData, err := h.prefixDb.Get(ctx, []byte("sym")) + voucherData, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err) return res, err @@ -1732,19 +1732,19 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by data := common.ProcessTransfers(transactionsResp) // Store all transaction data - dataMap := map[string]string{ - "txfrom": data.Senders, - "txto": data.Recipients, - "txval": data.TransferValues, - "txaddr": data.Addresses, - "txhash": data.TxHashes, - "txdate": data.Dates, - "txsym": data.Symbols, - "txdeci": data.Decimals, + dataMap := map[common.DataTyp]string{ + common.DATA_PREFIX_TX_SENDERS: data.Senders, + common.DATA_PREFIX_TX_RECIPIENTS: data.Recipients, + common.DATA_PREFIX_TX_VALUES: data.TransferValues, + common.DATA_PREFIX_TX_ADDRESSES: data.Addresses, + common.DATA_PREFIX_TX_HASHES: data.TxHashes, + common.DATA_PREFIX_TX_DATES: data.Dates, + common.DATA_PREFIX_TX_SYMBOLS: data.Symbols, + common.DATA_PREFIX_TX_DECIMALS: data.Decimals, } for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(key), []byte(value)); err != nil { + if err := h.prefixDb.Put(ctx, []byte(key.ToBytes()), []byte(value)); err != nil { logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) return res, err } @@ -1770,22 +1770,22 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] } // Read transactions from the store and format them - TransactionSenders, err := h.prefixDb.Get(ctx, []byte("txfrom")) + TransactionSenders, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_SENDERS.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) return res, err } - TransactionSyms, err := h.prefixDb.Get(ctx, []byte("txsym")) + TransactionSyms, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_SYMBOLS.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) return res, err } - TransactionValues, err := h.prefixDb.Get(ctx, []byte("txval")) + TransactionValues, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_VALUES.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) return res, err } - TransactionDates, err := h.prefixDb.Get(ctx, []byte("txdate")) + TransactionDates, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_DATES.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) return res, err From bc0e536d3da41c0cac3212bc9816be16c390aa44 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Dec 2024 20:55:03 +0300 Subject: [PATCH 232/332] updated failing tests --- common/transfer_statements.go | 2 +- common/vouchers_test.go | 12 ++++++------ internal/handlers/ussd/menuhandler_test.go | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index adbdeaf..dd8324a 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -63,7 +63,7 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, for _, key := range keys { value, err := db.Get(ctx, key.ToBytes()) if err != nil { - return "", fmt.Errorf("failed to get %s: %v", key, err) + return "", fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) } data[key] = string(value) } diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 9f5bf85..9ba7cbc 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -86,16 +86,16 @@ func TestGetVoucherData(t *testing.T) { spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) // Test voucher data - mockData := map[string][]byte{ - "sym": []byte("1:SRF\n2:MILO"), - "bal": []byte("1:100\n2:200"), - "deci": []byte("1:6\n2:4"), - "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + mockData := map[DataTyp][]byte{ + DATA_PREFIX_SYMBOLS: []byte("1:SRF\n2:MILO"), + DATA_PREFIX_BALANCES: []byte("1:100\n2:200"), + DATA_PREFIX_DECIMALS: []byte("1:6\n2:4"), + DATA_PREFIX_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), } // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(key), []byte(value)) + err = spdb.Put(ctx, []byte(key.ToBytes()), []byte(value)) if err != nil { t.Fatal(err) } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index d7cd855..4e53228 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1937,7 +1937,7 @@ func TestCheckVouchers(t *testing.T) { assert.NoError(t, err) // Read voucher sym data from the store - voucherData, err := spdb.Get(ctx, []byte("sym")) + voucherData, err := spdb.Get(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes()) if err != nil { t.Fatal(err) } @@ -1961,7 +1961,7 @@ func TestGetVoucherList(t *testing.T) { expectedSym := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err := spdb.Put(ctx, []byte("sym"), expectedSym) + err := spdb.Put(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes(), expectedSym) if err != nil { t.Fatal(err) } @@ -1991,16 +1991,16 @@ func TestViewVoucher(t *testing.T) { } // Define mock voucher data - mockData := map[string][]byte{ - "sym": []byte("1:SRF\n2:MILO"), - "bal": []byte("1:100\n2:200"), - "deci": []byte("1:6\n2:4"), - "addr": []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + mockData := map[common.DataTyp][]byte{ + common.DATA_PREFIX_SYMBOLS: []byte("1:SRF\n2:MILO"), + common.DATA_PREFIX_BALANCES: []byte("1:100\n2:200"), + common.DATA_PREFIX_DECIMALS: []byte("1:6\n2:4"), + common.DATA_PREFIX_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), } // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(key), []byte(value)) + err = spdb.Put(ctx, []byte(key.ToBytes()), []byte(value)) if err != nil { t.Fatal(err) } From 862830e9dedf20d2d9cf25c3ff738eb1428c3203 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Dec 2024 20:59:46 +0300 Subject: [PATCH 233/332] renamed internal/storage/db.go -> internal/storage/sub_prefix_db.go for clarity --- internal/storage/{db.go => sub_prefix_db.go} | 0 internal/storage/{db_test.go => sub_prefix_db_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename internal/storage/{db.go => sub_prefix_db.go} (100%) rename internal/storage/{db_test.go => sub_prefix_db_test.go} (100%) diff --git a/internal/storage/db.go b/internal/storage/sub_prefix_db.go similarity index 100% rename from internal/storage/db.go rename to internal/storage/sub_prefix_db.go diff --git a/internal/storage/db_test.go b/internal/storage/sub_prefix_db_test.go similarity index 100% rename from internal/storage/db_test.go rename to internal/storage/sub_prefix_db_test.go From 7a9de79aae67ee37369195899770a475f5cd8d55 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Dec 2024 21:07:11 +0300 Subject: [PATCH 234/332] return nil with error --- common/vouchers.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/common/vouchers.go b/common/vouchers.go index 5863c46..e3cc134 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -70,8 +70,7 @@ func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*da for _, key := range keys { value, err := db.Get(ctx, key.ToBytes()) if err != nil { - fmt.Printf("failed to get %v: %v\n", key, err) - continue + return nil, fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) } data[key] = string(value) } From 321f038c7ce4492d05bd35572e68d669b7c05a9b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 10:52:45 +0300 Subject: [PATCH 235/332] iterate over a map for the set profile items --- internal/handlers/ussd/menuhandler.go | 78 +++++++-------------------- 1 file changed, 18 insertions(+), 60 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 19f9f97..fef4d98 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1966,68 +1966,26 @@ func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res "flag_location_set", "flag_offerings_set", } + profileDataKeys := map[int]common.DataTyp{ + 0: common.DATA_FIRST_NAME, + 1: common.DATA_FAMILY_NAME, + 2: common.DATA_GENDER, + 3: common.DATA_YOB, + 4: common.DATA_LOCATION, + 5: common.DATA_OFFERINGS, + } for index, profileItem := range h.profile.ProfileItems { - switch index { - case 0: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_FIRST_NAME, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile first name entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) - } - case 1: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_FAMILY_NAME, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile family name entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) - } - case 2: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_GENDER, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile gender entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) - } - case 3: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_YOB, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile yob entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) - } - case 4: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_LOCATION, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile location entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) - } - case 5: - if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, common.DATA_OFFERINGS, []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile offerings entry with", "key", common.DATA_TEMPORARY_VALUE, "value", profileItem, "error", err) - return err - } - flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) + // Ensure the profileItem is not "0"(is set) + if profileItem != "0" { + err = store.WriteEntry(ctx, sessionId, profileDataKeys[index], []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile entry with", "key", profileDataKeys[index], "value", profileItem, "error", err) + return err } + + // Get the flag for the current index + flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) + res.FlagSet = append(res.FlagSet, flag) } } return nil From 3e7f90733eef244d93dc122217cb1787c4ce7e49 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 12:22:29 +0300 Subject: [PATCH 236/332] update test names --- menutraversal_test/group_test.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index c382816..8f43ff5 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -167,7 +167,7 @@ ] }, { - "name": "menu_my_account_edit_firstname", + "name": "menu_my_account_edit_all_account_details_starting_from_firstname", "steps": [ { "input": "", @@ -224,7 +224,7 @@ ] }, { - "name": "menu_my_account_edit_familyname", + "name": "menu_my_account_edit_familyname_when_all_account__details_have_been_set", "steps": [ { "input": "", @@ -262,7 +262,7 @@ ] }, { - "name": "menu_my_account_edit_gender", + "name": "menu_my_account_edit_gender_when_all_account__details_have_been_set", "steps": [ { "input": "", @@ -299,7 +299,7 @@ ] }, { - "name": "menu_my_account_edit_yob", + "name": "menu_my_account_edit_yob_when_all_account__details_have_been_set", "steps": [ { "input": "", @@ -336,7 +336,7 @@ ] }, { - "name": "menu_my_account_edit_location", + "name": "menu_my_account_edit_location_when_all_account_details_have_been_set", "steps": [ { "input": "", @@ -373,7 +373,7 @@ ] }, { - "name": "menu_my_account_edit_offerings", + "name": "menu_my_account_edit_offerings_when_all_account__details_have_been_set", "steps": [ { "input": "", From 22f96363ba61352cc23868f5f0d26b75915e0971 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 14:04:56 +0300 Subject: [PATCH 237/332] add test files --- .../profile_edit_start_familyname.json | 68 +++++++++++++++++++ .../profile_edit_start_firstname.json | 61 +++++++++++++++++ .../profile_edit_start_gender.json | 55 +++++++++++++++ .../profile_edit_start_location.json | 46 +++++++++++++ .../profile_edit_start_offerings.json | 42 ++++++++++++ .../profile_edit_start_yob.json | 50 ++++++++++++++ 6 files changed, 322 insertions(+) create mode 100644 menutraversal_test/profile_edit_start_familyname.json create mode 100644 menutraversal_test/profile_edit_start_firstname.json create mode 100644 menutraversal_test/profile_edit_start_gender.json create mode 100644 menutraversal_test/profile_edit_start_location.json create mode 100644 menutraversal_test/profile_edit_start_offerings.json create mode 100644 menutraversal_test/profile_edit_start_yob.json diff --git a/menutraversal_test/profile_edit_start_familyname.json b/menutraversal_test/profile_edit_start_familyname.json new file mode 100644 index 0000000..98325b0 --- /dev/null +++ b/menutraversal_test/profile_edit_start_familyname.json @@ -0,0 +1,68 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_family_name", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "2", + "expectedContent": "Enter family name:\n0:Back" + }, + { + "input": "bar", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + + + + + + + + + + + \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_firstname.json b/menutraversal_test/profile_edit_start_firstname.json new file mode 100644 index 0000000..0f6be8b --- /dev/null +++ b/menutraversal_test/profile_edit_start_firstname.json @@ -0,0 +1,61 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_firstname", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your first names:\n0:Back" + }, + { + "input": "foo", + "expectedContent": "Enter family name:\n0:Back" + }, + { + "input": "bar", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} diff --git a/menutraversal_test/profile_edit_start_gender.json b/menutraversal_test/profile_edit_start_gender.json new file mode 100644 index 0000000..afca12a --- /dev/null +++ b/menutraversal_test/profile_edit_start_gender.json @@ -0,0 +1,55 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_gender", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "3", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + + \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_location.json b/menutraversal_test/profile_edit_start_location.json new file mode 100644 index 0000000..8852911 --- /dev/null +++ b/menutraversal_test/profile_edit_start_location.json @@ -0,0 +1,46 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_location", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "5", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_offerings.json b/menutraversal_test/profile_edit_start_offerings.json new file mode 100644 index 0000000..6aa40f6 --- /dev/null +++ b/menutraversal_test/profile_edit_start_offerings.json @@ -0,0 +1,42 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_offerings", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "6", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + \ No newline at end of file diff --git a/menutraversal_test/profile_edit_start_yob.json b/menutraversal_test/profile_edit_start_yob.json new file mode 100644 index 0000000..45227f7 --- /dev/null +++ b/menutraversal_test/profile_edit_start_yob.json @@ -0,0 +1,50 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_all_account_details_starting_from_yob", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "4", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + \ No newline at end of file From 160ccbb220425e242016a3b7d675ca8fe26038e4 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 14:05:32 +0300 Subject: [PATCH 238/332] read test file from test run arg --- menutraversal_test/menu_traversal_test.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index d79b771..28d88db 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -3,6 +3,7 @@ package menutraversaltest import ( "bytes" "context" + "flag" "log" "math/rand" "os" @@ -15,14 +16,15 @@ import ( ) var ( - testData = driver.ReadData() - testStore = ".test_state" - groupTestFile = "group_test.json" - sessionID string - src = rand.NewSource(42) - g = rand.New(src) + testData = driver.ReadData() + testStore = ".test_state" + sessionID string + src = rand.NewSource(42) + g = rand.New(src) ) +var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests") + func GenerateSessionId() string { uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g)) v, err := uu.NewV4() @@ -337,7 +339,7 @@ func TestMainMenuSend(t *testing.T) { } func TestGroups(t *testing.T) { - groups, err := driver.LoadTestGroups(groupTestFile) + groups, err := driver.LoadTestGroups(*groupTestFile) if err != nil { log.Fatalf("Failed to load test groups: %v", err) } From 72a3681767572f225df983ba464d970e52a3386c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 16:03:08 +0300 Subject: [PATCH 239/332] add test data. --- .../profile_edit_when_adjacent_item_set.json | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 menutraversal_test/profile_edit_when_adjacent_item_set.json diff --git a/menutraversal_test/profile_edit_when_adjacent_item_set.json b/menutraversal_test/profile_edit_when_adjacent_item_set.json new file mode 100644 index 0000000..f8d7263 --- /dev/null +++ b/menutraversal_test/profile_edit_when_adjacent_item_set.json @@ -0,0 +1,70 @@ +{ + "groups": [ + { + "name": "menu_my_account_edit_familyname_when_adjacent_profile_information_set", + "steps": [ + { + "input": "", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + }, + { + "input": "3", + "expectedContent": "My Account\n1:Profile\n2:Change language\n3:Check balances\n4:Check statement\n5:PIN options\n6:My Address\n0:Back" + }, + { + "input": "1", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "3", + "expectedContent": "Select gender: \n1:Male\n2:Female\n3:Unspecified\n0:Back" + }, + { + "input": "1", + "expectedContent": "Enter your year of birth\n0:Back" + }, + { + "input": "1940", + "expectedContent": "Enter your location:\n0:Back" + }, + { + "input": "Kilifi", + "expectedContent": "Enter the services or goods you offer: \n0:Back" + }, + { + "input": "Bananas", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "2", + "expectedContent": "Enter family name:\n0:Back" + }, + { + "input": "foo2", + "expectedContent": "Please enter your PIN:" + }, + { + "input": "1234", + "expectedContent": "Profile updated successfully\n\n0:Back\n9:Quit" + }, + { + "input": "0", + "expectedContent": "My profile\n1:Edit name\n2:Edit family name\n3:Edit gender\n4:Edit year of birth\n5:Edit location\n6:Edit offerings\n7:View profile\n0:Back" + }, + { + "input": "0", + "expectedContent": "{balance}\n\n1:Send\n2:My Vouchers\n3:My Account\n4:Help\n9:Quit" + } + ] + } + ] +} + \ No newline at end of file From 18423fcd9c91a392f54d7c8ec5fdc0dc7d966d60 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 16:26:56 +0300 Subject: [PATCH 240/332] updated the name of the voucher related data --- common/db.go | 8 ++++---- common/vouchers.go | 12 ++++++------ common/vouchers_test.go | 10 +++++----- internal/handlers/ussd/menuhandler.go | 10 +++++----- internal/handlers/ussd/menuhandler_test.go | 12 ++++++------ 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/common/db.go b/common/db.go index 1454a84..6abfcb5 100644 --- a/common/db.go +++ b/common/db.go @@ -32,10 +32,10 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS - DATA_PREFIX_SYMBOLS - DATA_PREFIX_BALANCES - DATA_PREFIX_DECIMALS - DATA_PREFIX_ADDRESSES + DATA_VOUCHER_SYMBOLS + DATA_VOUCHER_BALANCES + DATA_VOUCHER_DECIMALS + DATA_VOUCHER_ADDRESSES DATA_PREFIX_TX_SENDERS DATA_PREFIX_TX_RECIPIENTS DATA_PREFIX_TX_VALUES diff --git a/common/vouchers.go b/common/vouchers.go index e3cc134..ab0e099 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -64,22 +64,22 @@ func ScaleDownBalance(balance, decimals string) string { // GetVoucherData retrieves and matches voucher data func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { - keys := []DataTyp{DATA_PREFIX_SYMBOLS, DATA_PREFIX_BALANCES, DATA_PREFIX_DECIMALS, DATA_PREFIX_ADDRESSES} + keys := []DataTyp{DATA_VOUCHER_SYMBOLS, DATA_VOUCHER_BALANCES, DATA_VOUCHER_DECIMALS, DATA_VOUCHER_ADDRESSES} data := make(map[DataTyp]string) for _, key := range keys { value, err := db.Get(ctx, key.ToBytes()) if err != nil { - return nil, fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) + return nil, fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) } data[key] = string(value) } symbol, balance, decimal, address := MatchVoucher(input, - data[DATA_PREFIX_SYMBOLS], - data[DATA_PREFIX_BALANCES], - data[DATA_PREFIX_DECIMALS], - data[DATA_PREFIX_ADDRESSES], + data[DATA_VOUCHER_SYMBOLS], + data[DATA_VOUCHER_BALANCES], + data[DATA_VOUCHER_DECIMALS], + data[DATA_VOUCHER_ADDRESSES], ) if symbol == "" { diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 9ba7cbc..e6f449d 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -8,8 +8,8 @@ import ( "github.com/alecthomas/assert/v2" "github.com/stretchr/testify/require" - "git.grassecon.net/urdt/ussd/internal/storage" memdb "git.defalsify.org/vise.git/db/mem" + "git.grassecon.net/urdt/ussd/internal/storage" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -87,10 +87,10 @@ func TestGetVoucherData(t *testing.T) { // Test voucher data mockData := map[DataTyp][]byte{ - DATA_PREFIX_SYMBOLS: []byte("1:SRF\n2:MILO"), - DATA_PREFIX_BALANCES: []byte("1:100\n2:200"), - DATA_PREFIX_DECIMALS: []byte("1:6\n2:4"), - DATA_PREFIX_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), + DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), + DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), + DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), } // Put the data diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index df3c9ae..14cfc50 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1577,10 +1577,10 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) // Store all voucher data dataMap := map[common.DataTyp]string{ - common.DATA_PREFIX_SYMBOLS: data.Symbols, - common.DATA_PREFIX_BALANCES: data.Balances, - common.DATA_PREFIX_DECIMALS: data.Decimals, - common.DATA_PREFIX_ADDRESSES: data.Addresses, + common.DATA_VOUCHER_SYMBOLS: data.Symbols, + common.DATA_VOUCHER_BALANCES: data.Balances, + common.DATA_VOUCHER_DECIMALS: data.Decimals, + common.DATA_VOUCHER_ADDRESSES: data.Addresses, } for key, value := range dataMap { @@ -1597,7 +1597,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) var res resource.Result // Read vouchers from the store - voucherData, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes()) + voucherData, err := h.prefixDb.Get(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes()) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err) return res, err diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 4e53228..9fabe97 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1937,7 +1937,7 @@ func TestCheckVouchers(t *testing.T) { assert.NoError(t, err) // Read voucher sym data from the store - voucherData, err := spdb.Get(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes()) + voucherData, err := spdb.Get(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes()) if err != nil { t.Fatal(err) } @@ -1961,7 +1961,7 @@ func TestGetVoucherList(t *testing.T) { expectedSym := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err := spdb.Put(ctx, common.DATA_PREFIX_SYMBOLS.ToBytes(), expectedSym) + err := spdb.Put(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes(), expectedSym) if err != nil { t.Fatal(err) } @@ -1992,10 +1992,10 @@ func TestViewVoucher(t *testing.T) { // Define mock voucher data mockData := map[common.DataTyp][]byte{ - common.DATA_PREFIX_SYMBOLS: []byte("1:SRF\n2:MILO"), - common.DATA_PREFIX_BALANCES: []byte("1:100\n2:200"), - common.DATA_PREFIX_DECIMALS: []byte("1:6\n2:4"), - common.DATA_PREFIX_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), + common.DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), + common.DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), + common.DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), + common.DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), } // Put the data From f733fe563601b6fd8bb19e297147664a69938d31 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 16:31:47 +0300 Subject: [PATCH 241/332] Start the sub prefix data at 256 (0x0100) --- common/db.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/db.go b/common/db.go index 6abfcb5..b1d67fd 100644 --- a/common/db.go +++ b/common/db.go @@ -32,7 +32,8 @@ const ( DATA_PUBLIC_KEY_REVERSE DATA_ACTIVE_DECIMAL DATA_ACTIVE_ADDRESS - DATA_VOUCHER_SYMBOLS + // Start the sub prefix data at 256 (0x0100) + DATA_VOUCHER_SYMBOLS DataTyp = 256 + iota DATA_VOUCHER_BALANCES DATA_VOUCHER_DECIMALS DATA_VOUCHER_ADDRESSES From a659fb06fa463c42dfd290ee32d1ba8693972dd4 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 16:58:03 +0300 Subject: [PATCH 242/332] Added a function to convert int to []byte --- common/db.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/db.go b/common/db.go index b1d67fd..80757e9 100644 --- a/common/db.go +++ b/common/db.go @@ -88,3 +88,10 @@ func (d DataTyp) ToBytes() []byte { binary.BigEndian.PutUint16(bytes, uint16(d)) return bytes } + +// Convert int to []byte +func IntToBytes(value int) []byte { + bytes := make([]byte, 2) + binary.BigEndian.PutUint16(bytes, uint16(value)) + return bytes +} From 589a94216b93dd8592645953c89c6af3a80ab549 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 17:02:26 +0300 Subject: [PATCH 243/332] Use the DATATYPE_USERDATA as the prefix for the NewSubPrefixDb func --- common/vouchers_test.go | 5 ++++- internal/handlers/ussd/menuhandler.go | 6 ++++-- internal/handlers/ussd/menuhandler_test.go | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/common/vouchers_test.go b/common/vouchers_test.go index e6f449d..560fb5b 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -8,6 +8,7 @@ import ( "github.com/alecthomas/assert/v2" "github.com/stretchr/testify/require" + visedb "git.defalsify.org/vise.git/db" memdb "git.defalsify.org/vise.git/db/mem" "git.grassecon.net/urdt/ussd/internal/storage" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" @@ -83,7 +84,9 @@ func TestGetVoucherData(t *testing.T) { if err != nil { t.Fatal(err) } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + + prefix := IntToBytes(visedb.DATATYPE_USERDATA) + spdb := storage.NewSubPrefixDb(db, prefix) // Test voucher data mockData := map[DataTyp][]byte{ diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 14cfc50..812c897 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -85,8 +85,10 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util userDb := &common.UserDataStore{ Db: userdataStore, } - // Instantiate the SubPrefixDb with "vouchers" prefix - prefixDb := storage.NewSubPrefixDb(userdataStore, []byte("vouchers")) + + // Instantiate the SubPrefixDb with "DATATYPE_USERDATA" prefix + prefix := common.IntToBytes(db.DATATYPE_USERDATA) + prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ userdataStore: userDb, diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 9fabe97..1aa7c28 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -22,6 +22,7 @@ import ( testdataloader "github.com/peteole/testdata-loader" "github.com/stretchr/testify/require" + visedb "git.defalsify.org/vise.git/db" memdb "git.defalsify.org/vise.git/db/mem" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -56,7 +57,8 @@ func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storage.SubPr if err != nil { t.Fatal(err) } - spdb := storage.NewSubPrefixDb(db, []byte("vouchers")) + prefix := common.IntToBytes(visedb.DATATYPE_USERDATA) + spdb := storage.NewSubPrefixDb(db, prefix) return spdb } From caff27b43da33c015eb13ef0fe10483bd88078e6 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 17:50:40 +0300 Subject: [PATCH 244/332] Replace IntToBytes(value int) and ToBytes() with a single ToBytes() function --- common/db.go | 11 ++--------- common/transfer_statements.go | 4 ++-- common/vouchers.go | 4 ++-- common/vouchers_test.go | 4 ++-- internal/handlers/ussd/menuhandler.go | 16 ++++++++-------- internal/handlers/ussd/menuhandler_test.go | 8 ++++---- 6 files changed, 20 insertions(+), 27 deletions(-) diff --git a/common/db.go b/common/db.go index 80757e9..b8fdb51 100644 --- a/common/db.go +++ b/common/db.go @@ -82,15 +82,8 @@ func StringToDataTyp(str string) (DataTyp, error) { } } -// Convert DataTyp to []byte -func (d DataTyp) ToBytes() []byte { - bytes := make([]byte, 2) - binary.BigEndian.PutUint16(bytes, uint16(d)) - return bytes -} - -// Convert int to []byte -func IntToBytes(value int) []byte { +// ToBytes converts DataTyp or int to a byte slice +func ToBytes[T ~uint16 | int](value T) []byte { bytes := make([]byte, 2) binary.BigEndian.PutUint16(bytes, uint16(value)) return bytes diff --git a/common/transfer_statements.go b/common/transfer_statements.go index dd8324a..8567557 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -61,9 +61,9 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, data := make(map[DataTyp]string) for _, key := range keys { - value, err := db.Get(ctx, key.ToBytes()) + value, err := db.Get(ctx, ToBytes(key)) if err != nil { - return "", fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) + return "", fmt.Errorf("failed to get %s: %v", ToBytes(key), err) } data[key] = string(value) } diff --git a/common/vouchers.go b/common/vouchers.go index ab0e099..6cff91d 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -68,9 +68,9 @@ func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*da data := make(map[DataTyp]string) for _, key := range keys { - value, err := db.Get(ctx, key.ToBytes()) + value, err := db.Get(ctx, ToBytes(key)) if err != nil { - return nil, fmt.Errorf("failed to get %s: %v", key.ToBytes(), err) + return nil, fmt.Errorf("failed to get %s: %v", ToBytes(key), err) } data[key] = string(value) } diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 560fb5b..ba6cd60 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -85,7 +85,7 @@ func TestGetVoucherData(t *testing.T) { t.Fatal(err) } - prefix := IntToBytes(visedb.DATATYPE_USERDATA) + prefix := ToBytes(visedb.DATATYPE_USERDATA) spdb := storage.NewSubPrefixDb(db, prefix) // Test voucher data @@ -98,7 +98,7 @@ func TestGetVoucherData(t *testing.T) { // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(key.ToBytes()), []byte(value)) + err = spdb.Put(ctx, []byte(ToBytes(key)), []byte(value)) if err != nil { t.Fatal(err) } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 812c897..c860771 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -87,7 +87,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util } // Instantiate the SubPrefixDb with "DATATYPE_USERDATA" prefix - prefix := common.IntToBytes(db.DATATYPE_USERDATA) + prefix := common.ToBytes(db.DATATYPE_USERDATA) prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ @@ -1586,7 +1586,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) } for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(key.ToBytes()), []byte(value)); err != nil { + if err := h.prefixDb.Put(ctx, []byte(common.ToBytes(key)), []byte(value)); err != nil { return res, nil } } @@ -1599,7 +1599,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) var res resource.Result // Read vouchers from the store - voucherData, err := h.prefixDb.Get(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes()) + voucherData, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the voucherData from prefixDb", "error", err) return res, err @@ -1757,7 +1757,7 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by } for key, value := range dataMap { - if err := h.prefixDb.Put(ctx, []byte(key.ToBytes()), []byte(value)); err != nil { + if err := h.prefixDb.Put(ctx, []byte(common.ToBytes(key)), []byte(value)); err != nil { logg.ErrorCtxf(ctx, "failed to write to prefixDb", "error", err) return res, err } @@ -1783,22 +1783,22 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] } // Read transactions from the store and format them - TransactionSenders, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_SENDERS.ToBytes()) + TransactionSenders, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_SENDERS)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) return res, err } - TransactionSyms, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_SYMBOLS.ToBytes()) + TransactionSyms, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_SYMBOLS)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) return res, err } - TransactionValues, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_VALUES.ToBytes()) + TransactionValues, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_VALUES)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) return res, err } - TransactionDates, err := h.prefixDb.Get(ctx, common.DATA_PREFIX_TX_DATES.ToBytes()) + TransactionDates, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_DATES)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) return res, err diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 1aa7c28..fac95a1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -57,7 +57,7 @@ func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storage.SubPr if err != nil { t.Fatal(err) } - prefix := common.IntToBytes(visedb.DATATYPE_USERDATA) + prefix := common.ToBytes(visedb.DATATYPE_USERDATA) spdb := storage.NewSubPrefixDb(db, prefix) return spdb @@ -1939,7 +1939,7 @@ func TestCheckVouchers(t *testing.T) { assert.NoError(t, err) // Read voucher sym data from the store - voucherData, err := spdb.Get(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes()) + voucherData, err := spdb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS)) if err != nil { t.Fatal(err) } @@ -1963,7 +1963,7 @@ func TestGetVoucherList(t *testing.T) { expectedSym := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err := spdb.Put(ctx, common.DATA_VOUCHER_SYMBOLS.ToBytes(), expectedSym) + err := spdb.Put(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS), expectedSym) if err != nil { t.Fatal(err) } @@ -2002,7 +2002,7 @@ func TestViewVoucher(t *testing.T) { // Put the data for key, value := range mockData { - err = spdb.Put(ctx, []byte(key.ToBytes()), []byte(value)) + err = spdb.Put(ctx, []byte(common.ToBytes(key)), []byte(value)) if err != nil { t.Fatal(err) } From 14737b5f1254fc5ee9ada948ab6e6811baf9d0f9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 5 Dec 2024 17:58:51 +0300 Subject: [PATCH 245/332] Removed redundant naming of transfer related data --- common/db.go | 16 ++++++++-------- common/transfer_statements.go | 16 ++++++++-------- internal/handlers/ussd/menuhandler.go | 24 ++++++++++++------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/common/db.go b/common/db.go index b8fdb51..5532dc9 100644 --- a/common/db.go +++ b/common/db.go @@ -37,14 +37,14 @@ const ( DATA_VOUCHER_BALANCES DATA_VOUCHER_DECIMALS DATA_VOUCHER_ADDRESSES - DATA_PREFIX_TX_SENDERS - DATA_PREFIX_TX_RECIPIENTS - DATA_PREFIX_TX_VALUES - DATA_PREFIX_TX_ADDRESSES - DATA_PREFIX_TX_HASHES - DATA_PREFIX_TX_DATES - DATA_PREFIX_TX_SYMBOLS - DATA_PREFIX_TX_DECIMALS + DATA_TX_SENDERS + DATA_TX_RECIPIENTS + DATA_TX_VALUES + DATA_TX_ADDRESSES + DATA_TX_HASHES + DATA_TX_DATES + DATA_TX_SYMBOLS + DATA_TX_DECIMALS ) var ( diff --git a/common/transfer_statements.go b/common/transfer_statements.go index 8567557..4e6f66b 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -57,7 +57,7 @@ func ProcessTransfers(transfers []dataserviceapi.Last10TxResponse) TransferMetad // GetTransferData retrieves and matches transfer data // returns a formatted string of the full transaction/statement func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, index int) (string, error) { - keys := []DataTyp{DATA_PREFIX_TX_SENDERS, DATA_PREFIX_TX_RECIPIENTS, DATA_PREFIX_TX_VALUES, DATA_PREFIX_TX_ADDRESSES, DATA_PREFIX_TX_HASHES, DATA_PREFIX_TX_DATES, DATA_PREFIX_TX_SYMBOLS} + keys := []DataTyp{DATA_TX_SENDERS, DATA_TX_RECIPIENTS, DATA_TX_VALUES, DATA_TX_ADDRESSES, DATA_TX_HASHES, DATA_TX_DATES, DATA_TX_SYMBOLS} data := make(map[DataTyp]string) for _, key := range keys { @@ -69,13 +69,13 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, } // Split the data - senders := strings.Split(string(data[DATA_PREFIX_TX_SENDERS]), "\n") - recipients := strings.Split(string(data[DATA_PREFIX_TX_RECIPIENTS]), "\n") - values := strings.Split(string(data[DATA_PREFIX_TX_VALUES]), "\n") - addresses := strings.Split(string(data[DATA_PREFIX_TX_ADDRESSES]), "\n") - hashes := strings.Split(string(data[DATA_PREFIX_TX_HASHES]), "\n") - dates := strings.Split(string(data[DATA_PREFIX_TX_DATES]), "\n") - syms := strings.Split(string(data[DATA_PREFIX_TX_SYMBOLS]), "\n") + senders := strings.Split(string(data[DATA_TX_SENDERS]), "\n") + recipients := strings.Split(string(data[DATA_TX_RECIPIENTS]), "\n") + values := strings.Split(string(data[DATA_TX_VALUES]), "\n") + addresses := strings.Split(string(data[DATA_TX_ADDRESSES]), "\n") + hashes := strings.Split(string(data[DATA_TX_HASHES]), "\n") + dates := strings.Split(string(data[DATA_TX_DATES]), "\n") + syms := strings.Split(string(data[DATA_TX_SYMBOLS]), "\n") // Check if index is within range if index < 1 || index > len(senders) { diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c860771..b47e31f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1746,14 +1746,14 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by // Store all transaction data dataMap := map[common.DataTyp]string{ - common.DATA_PREFIX_TX_SENDERS: data.Senders, - common.DATA_PREFIX_TX_RECIPIENTS: data.Recipients, - common.DATA_PREFIX_TX_VALUES: data.TransferValues, - common.DATA_PREFIX_TX_ADDRESSES: data.Addresses, - common.DATA_PREFIX_TX_HASHES: data.TxHashes, - common.DATA_PREFIX_TX_DATES: data.Dates, - common.DATA_PREFIX_TX_SYMBOLS: data.Symbols, - common.DATA_PREFIX_TX_DECIMALS: data.Decimals, + common.DATA_TX_SENDERS: data.Senders, + common.DATA_TX_RECIPIENTS: data.Recipients, + common.DATA_TX_VALUES: data.TransferValues, + common.DATA_TX_ADDRESSES: data.Addresses, + common.DATA_TX_HASHES: data.TxHashes, + common.DATA_TX_DATES: data.Dates, + common.DATA_TX_SYMBOLS: data.Symbols, + common.DATA_TX_DECIMALS: data.Decimals, } for key, value := range dataMap { @@ -1783,22 +1783,22 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] } // Read transactions from the store and format them - TransactionSenders, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_SENDERS)) + TransactionSenders, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_TX_SENDERS)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSenders from prefixDb", "error", err) return res, err } - TransactionSyms, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_SYMBOLS)) + TransactionSyms, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_TX_SYMBOLS)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionSyms from prefixDb", "error", err) return res, err } - TransactionValues, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_VALUES)) + TransactionValues, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_TX_VALUES)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionValues from prefixDb", "error", err) return res, err } - TransactionDates, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_PREFIX_TX_DATES)) + TransactionDates, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_TX_DATES)) if err != nil { logg.ErrorCtxf(ctx, "Failed to read the TransactionDates from prefixDb", "error", err) return res, err From 9cbbdff993026f33e05d0eaab7de32dc6a6c7392 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 5 Dec 2024 18:25:51 +0300 Subject: [PATCH 246/332] chore: use profileDataKeys as an array --- internal/handlers/ussd/menuhandler.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index fef4d98..4a1774e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1966,13 +1966,13 @@ func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res "flag_location_set", "flag_offerings_set", } - profileDataKeys := map[int]common.DataTyp{ - 0: common.DATA_FIRST_NAME, - 1: common.DATA_FAMILY_NAME, - 2: common.DATA_GENDER, - 3: common.DATA_YOB, - 4: common.DATA_LOCATION, - 5: common.DATA_OFFERINGS, + profileDataKeys := []common.DataTyp{ + common.DATA_FIRST_NAME, + common.DATA_FAMILY_NAME, + common.DATA_GENDER, + common.DATA_YOB, + common.DATA_LOCATION, + common.DATA_OFFERINGS, } for index, profileItem := range h.profile.ProfileItems { // Ensure the profileItem is not "0"(is set) From 3416fdf50c2ff82fadf9b4cc5afcf38b149de9c6 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 7 Dec 2024 23:06:03 +0000 Subject: [PATCH 247/332] Add keyinfo restore --- common/user_store.go | 4 +-- debug/db.go | 22 ++++++++++++---- debug/db_debug.go | 60 +++++++++++++++++++++++++------------------ debug/db_test.go | 44 ++++++++++++++++++++++++++------ devtools/gen/main.go | 61 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 40 deletions(-) create mode 100644 devtools/gen/main.go diff --git a/common/user_store.go b/common/user_store.go index 29796e2..6c770d8 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -20,7 +20,7 @@ type UserDataStore struct { 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)) + k := ToBytes(typ) return store.Get(ctx, k) } @@ -29,6 +29,6 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ 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)) + k := ToBytes(typ) return store.Put(ctx, k, value) } diff --git a/debug/db.go b/debug/db.go index ccc8630..eb62a41 100644 --- a/debug/db.go +++ b/debug/db.go @@ -4,8 +4,8 @@ import ( "fmt" "encoding/binary" - "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/common" + "git.defalsify.org/vise.git/db" ) var ( @@ -30,11 +30,12 @@ func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { o.SessionId = sessionId - k = k[len(b):] - o.Typ = k[0] + o.Typ = uint8(k[0]) k = k[1:] + o.SessionId = string(k[:len(b)]) + k = k[len(b):] - if o.Typ == storage.DATATYPE_USERSUB { + if o.Typ == db.DATATYPE_USERDATA { if len(k) == 0 { return o, fmt.Errorf("missing subtype key") } @@ -53,8 +54,19 @@ func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { return o, nil } +func FromKey(k []byte) (KeyInfo, error) { + o := KeyInfo{} + + if len(k) < 4 { + return o, fmt.Errorf("insufficient key length") + } + + sessionIdBytes := k[1:len(k)-2] + return ToKeyInfo(k, string(sessionIdBytes)) +} + func subTypToString(v common.DataTyp) string { - return dbTypStr[v + storage.DATATYPE_USERSUB + 1] + return dbTypStr[v + db.DATATYPE_USERDATA + 1] } func typToString(v uint8) string { diff --git a/debug/db_debug.go b/debug/db_debug.go index 7c17eed..ed2dd66 100644 --- a/debug/db_debug.go +++ b/debug/db_debug.go @@ -6,33 +6,43 @@ import ( "git.defalsify.org/vise.git/db" "git.grassecon.net/urdt/ussd/common" - "git.grassecon.net/urdt/ussd/internal/storage" ) func init() { DebugCap |= 1 - dbTypStr[db.DATATYPE_STATE] = "internal_state" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account_created" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_LOCATION] = "location" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_GENDER] = "gender" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_OFFERINGS] = "offerings" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_RECIPIENT] = "recipient" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_AMOUNT] = "amount" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_SYM] = "active sym" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_BAL] = "active bal" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_ADDRESS] = "active address" - dbTypStr[storage.DATATYPE_USERDATA + 1 + common.DATA_TRANSACTIONS] = "transactions" + dbTypStr[db.DATATYPE_STATE] = "internal state" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account created" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_LOCATION] = "location" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_GENDER] = "gender" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_OFFERINGS] = "offerings" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_RECIPIENT] = "recipient" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_AMOUNT] = "amount" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_SYM] = "active sym" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_BAL] = "active bal" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_ADDRESS] = "active address" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_SYMBOLS] = "voucher symbols" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_BALANCES] = "voucher balances" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_DECIMALS] = "voucher decimals" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_ADDRESSES] = "voucher addresses" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SENDERS] = "tx senders" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_RECIPIENTS] = "tx recipients" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_VALUES] = "tx values" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_ADDRESSES] = "tx addresses" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_HASHES] = "tx hashes" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DATES] = "tx dates" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SYMBOLS] = "tx symbols" + dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DECIMALS] = "tx decimals" } diff --git a/debug/db_test.go b/debug/db_test.go index 2dae1e8..25f781d 100644 --- a/debug/db_test.go +++ b/debug/db_test.go @@ -2,12 +2,16 @@ package debug import ( "testing" + + "git.grassecon.net/urdt/ussd/common" + "git.defalsify.org/vise.git/db" ) func TestDebugDbSubKeyInfo(t *testing.T) { s := "foo" - b := []byte(s) - b = append(b, []byte{0x40, 0x00, 0x02}...) + b := []byte{0x20} + b = append(b, []byte(s)...) + b = append(b, []byte{0x00, 0x02}...) r, err := ToKeyInfo(b, s) if err != nil { t.Fatal(err) @@ -15,7 +19,7 @@ func TestDebugDbSubKeyInfo(t *testing.T) { if r.SessionId != s { t.Fatalf("expected %s, got %s", s, r.SessionId) } - if r.Typ != 64 { + if r.Typ != 32 { t.Fatalf("expected 64, got %d", r.Typ) } if r.SubTyp != 2 { @@ -30,8 +34,32 @@ func TestDebugDbSubKeyInfo(t *testing.T) { func TestDebugDbKeyInfo(t *testing.T) { s := "bar" - b := []byte(s) - b = append(b, []byte{0x20}...) + b := []byte{0x10} + b = append(b, []byte(s)...) + r, err := ToKeyInfo(b, s) + if err != nil { + t.Fatal(err) + } + if r.SessionId != s { + t.Fatalf("expected %s, got %s", s, r.SessionId) + } + if r.Typ != 16 { + t.Fatalf("expected 16, got %d", r.Typ) + } + if DebugCap & 1 > 0 { + if r.Label != "internal state" { + t.Fatalf("expected 'internal_state', got '%s'", r.Label) + } + } +} + +func TestDebugDbKeyInfoRestore(t *testing.T) { + s := "bar" + b := []byte{db.DATATYPE_USERDATA} + b = append(b, []byte(s)...) + k := common.ToBytes(common.DATA_ACTIVE_SYM) + b = append(b, k...) + r, err := ToKeyInfo(b, s) if err != nil { t.Fatal(err) @@ -40,11 +68,11 @@ func TestDebugDbKeyInfo(t *testing.T) { t.Fatalf("expected %s, got %s", s, r.SessionId) } if r.Typ != 32 { - t.Fatalf("expected 64, got %d", r.Typ) + t.Fatalf("expected 32, got %d", r.Typ) } if DebugCap & 1 > 0 { - if r.Label != "userdata" { - t.Fatalf("expected 'userdata', got '%s'", r.Label) + if r.Label != "active sym" { + t.Fatalf("expected 'active sym', got '%s'", r.Label) } } } diff --git a/devtools/gen/main.go b/devtools/gen/main.go new file mode 100644 index 0000000..401f194 --- /dev/null +++ b/devtools/gen/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "path" + + "git.defalsify.org/vise.git/logging" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/common" +) + +var ( + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") +) + +func main() { + config.LoadConfig() + + var dbDir string + var sessionId string + var database string + var engineDebug bool + + flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") + flag.StringVar(&database, "db", "gdbm", "database to be used") + flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") + flag.BoolVar(&engineDebug, "d", false, "use engine debug output") + flag.Parse() + + ctx := context.Background() + ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Database", database) + + resourceDir := scriptDir + menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) + + store, err := menuStorageService.GetUserdataDb(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + userStore := common.UserDataStore{store} + + err = userStore.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("1.0")) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + err = store.Close() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + +} From 890f50704f20e518d9580dcf1436159ff047569a Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 8 Dec 2024 21:21:48 +0000 Subject: [PATCH 248/332] Add documentation comments for db subprefix types --- common/db.go | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/common/db.go b/common/db.go index 5532dc9..f20dbab 100644 --- a/common/db.go +++ b/common/db.go @@ -7,43 +7,88 @@ import ( "git.defalsify.org/vise.git/logging" ) +// DataType is a subprefix value used in association with vise/db.DATATYPE_USERDATA. +// +// All keys are used only within the context of a single account. Unless otherwise specified, the user context is the session id. +// +// * The first byte is vise/db.DATATYPE_USERDATA +// * The last 2 bytes are the DataTyp value, big-endian. +// * The intermediate bytes are the id of the user context. +// +// All values are strings type DataTyp uint16 const ( + // TODO: Seems unused DATA_ACCOUNT DataTyp = iota + // TODO: Seems unused, only read not written DATA_ACCOUNT_CREATED + // API Tracking id to follow status of account creation DATA_TRACKING_ID + // EVM address returned from API on account creation DATA_PUBLIC_KEY + // TODO: Seems unused DATA_CUSTODIAL_ID + // Currently active PIN used to authenticate ussd state change requests DATA_ACCOUNT_PIN + // TODO: Seems unused DATA_ACCOUNT_STATUS + // The first name of the user DATA_FIRST_NAME + // The last name of the user DATA_FAMILY_NAME + // The year-of-birth of the user DATA_YOB + // The location of the user DATA_LOCATION + // The gender of the user DATA_GENDER + // The offerings description of the user DATA_OFFERINGS + // The ethereum address of the recipient of an ongoing send request DATA_RECIPIENT + // The voucher value amount of an ongoing send request DATA_AMOUNT + // A general swap field for temporary values DATA_TEMPORARY_VALUE + // Currently active voucher symbol of user DATA_ACTIVE_SYM + // Voucher balance of user's currently active voucher DATA_ACTIVE_BAL + // String boolean indicating whether use of PIN is blocked DATA_BLOCKED_NUMBER + // Reverse mapping of a user's evm address to a session id. DATA_PUBLIC_KEY_REVERSE + // Decimal count of the currently active voucher DATA_ACTIVE_DECIMAL + // EVM address of the currently active voucher DATA_ACTIVE_ADDRESS - // Start the sub prefix data at 256 (0x0100) +) + +const ( + // List of valid voucher symbols in the user context. DATA_VOUCHER_SYMBOLS DataTyp = 256 + iota + // List of voucher balances for vouchers valid in the user context. DATA_VOUCHER_BALANCES + // List of voucher decimal counts for vouchers valid in the user context. DATA_VOUCHER_DECIMALS + // List of voucher EVM addresses for vouchers valid in the user context. DATA_VOUCHER_ADDRESSES + // List of senders for valid transactions in the user context. DATA_TX_SENDERS + // List of recipients for valid transactions in the user context. DATA_TX_RECIPIENTS + // List of voucher values for valid transactions in the user context. DATA_TX_VALUES + // List of voucher EVM addresses for valid transactions in the user context. DATA_TX_ADDRESSES + // List of valid transaction hashes in the user context. DATA_TX_HASHES + // List of transaction dates for valid transactions in the user context. DATA_TX_DATES + // List of voucher symbols for valid transactions in the user context. DATA_TX_SYMBOLS + // List of voucher decimal counts for valid transactions in the user context. DATA_TX_DECIMALS ) From a3e821fb16f5cb994175d042a39e4e43eca21121 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 8 Dec 2024 21:57:53 +0000 Subject: [PATCH 249/332] Clarify gen cmd example with reverse public key lookup --- devtools/gen/main.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/devtools/gen/main.go b/devtools/gen/main.go index 401f194..6b58a50 100644 --- a/devtools/gen/main.go +++ b/devtools/gen/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "crypto/sha1" "flag" "fmt" "os" @@ -46,7 +47,18 @@ func main() { } userStore := common.UserDataStore{store} - err = userStore.WriteEntry(ctx, sessionId, common.DATA_AMOUNT, []byte("1.0")) + h := sha1.New() + h.Write([]byte(sessionId)) + address := h.Sum(nil) + addressString := fmt.Sprintf("%x", address) + + err = userStore.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(addressString)) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + err = userStore.WriteEntry(ctx, addressString, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId)) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) From 604c16ec90ddcf2e97719c0893c0747a945bf681 Mon Sep 17 00:00:00 2001 From: lash Date: Sun, 8 Dec 2024 22:48:39 +0000 Subject: [PATCH 250/332] Habilitate store dumper --- debug/db.go | 10 ++++++++++ devtools/gen/main.go | 6 ++++++ devtools/store/main.go | 11 +++++++++-- go.mod | 2 +- go.sum | 4 ++-- internal/storage/gdbm.go | 2 ++ 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/debug/db.go b/debug/db.go index eb62a41..ec9e58f 100644 --- a/debug/db.go +++ b/debug/db.go @@ -20,6 +20,16 @@ type KeyInfo struct { Description string } +func (k KeyInfo) String() string { + v := uint16(k.SubTyp) + s := subTypToString(k.SubTyp) + if s == "" { + v = uint16(k.Typ) + s = typToString(k.Typ) + } + return fmt.Sprintf("Session Id: %s\nTyp: %s (%d)\n", k.SessionId, s, v) +} + func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) { o := KeyInfo{} b := []byte(sessionId) diff --git a/devtools/gen/main.go b/devtools/gen/main.go index 6b58a50..b9e2aed 100644 --- a/devtools/gen/main.go +++ b/devtools/gen/main.go @@ -11,6 +11,7 @@ import ( "git.defalsify.org/vise.git/logging" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/common" ) @@ -19,6 +20,11 @@ var ( scriptDir = path.Join("services", "registration") ) +func init() { + initializers.LoadEnvVariables() +} + + func main() { config.LoadConfig() diff --git a/devtools/store/main.go b/devtools/store/main.go index d6abec9..9262703 100644 --- a/devtools/store/main.go +++ b/devtools/store/main.go @@ -10,6 +10,7 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/storage" + "git.grassecon.net/urdt/ussd/debug" "git.defalsify.org/vise.git/logging" ) @@ -30,6 +31,7 @@ func main() { var sessionId string var database string var engineDebug bool + flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") @@ -37,7 +39,7 @@ func main() { flag.Parse() ctx := context.Background() - //ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) resourceDir := scriptDir @@ -60,7 +62,12 @@ func main() { if k == nil { break } - fmt.Printf("%x %s %x\n", k, v) + o, err := debug.FromKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + fmt.Printf("%vValue: %v\n\n", o, v) } err = store.Close() diff --git a/go.mod b/go.mod index d89f0f3..aeac055 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a + git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5 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 diff --git a/go.sum b/go.sum index 5532c1c..5949d8b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE= -git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5 h1:QI/6xmpFM9NsG1+vRPzlUkkjAQ1Z8WhNqS+2tCjBpxg= +git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go index 9879fe0..31ebf47 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm.go @@ -116,5 +116,7 @@ func(tdb *ThreadGdbmDb) Close() error { } func(tdb *ThreadGdbmDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) { + tdb.reserve() + defer tdb.release() return tdb.db.Dump(ctx, key) } From 5d8de80a181971a61bb8de3cef894fafc2700b86 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 18:58:50 +0300 Subject: [PATCH 251/332] Write the error in the response --- internal/http/server.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/internal/http/server.go b/internal/http/server.go index 3ea0159..a6239c4 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -17,8 +17,7 @@ var ( type DefaultRequestParser struct { } - -func(rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { +func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { return "", handlers.ErrInvalidRequest @@ -30,7 +29,7 @@ func(rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { return v, nil } -func(rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) { +func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) { rqv, ok := rq.(*http.Request) if !ok { return nil, handlers.ErrInvalidRequest @@ -53,25 +52,24 @@ func ToSessionHandler(h handlers.RequestHandler) *SessionHandler { } } -func(f *SessionHandler) writeError(w http.ResponseWriter, code int, err error) { +func (f *SessionHandler) 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{}) + _, err = w.Write([]byte(s)) if err != nil { logg.Errorf("error writing error!!", "err", err, "olderr", s) w.WriteHeader(500) } - return } -func(f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { var code int var err error var perr error rqs := handlers.RequestSession{ - Ctx: req.Context(), + Ctx: req.Context(), Writer: w, } From 3129e8210e848185701c328f1bf053e375d9b9ae Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:25:38 +0300 Subject: [PATCH 252/332] Capitalize the transfer statement details --- common/transfer_statements.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/transfer_statements.go b/common/transfer_statements.go index 4e6f66b..243ef4c 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -84,18 +84,18 @@ func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, // Adjust for 0-based indexing i := index - 1 - transactionType := "received" - party := fmt.Sprintf("from: %s", strings.TrimSpace(senders[i])) + transactionType := "Received" + party := fmt.Sprintf("From: %s", strings.TrimSpace(senders[i])) if strings.TrimSpace(senders[i]) == publicKey { - transactionType = "sent" - party = fmt.Sprintf("to: %s", strings.TrimSpace(recipients[i])) + transactionType = "Sent" + party = fmt.Sprintf("To: %s", strings.TrimSpace(recipients[i])) } formattedDate := formatDate(strings.TrimSpace(dates[i])) // Build the full transaction detail detail := fmt.Sprintf( - "%s %s %s\n%s\ncontract address: %s\ntxhash: %s\ndate: %s", + "%s %s %s\n%s\nContract address: %s\nTxhash: %s\nDate: %s", transactionType, strings.TrimSpace(values[i]), strings.TrimSpace(syms[i]), From b1e4b63c6a3bd25998311aca149e08fa49ab7753 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:28:53 +0300 Subject: [PATCH 253/332] Add a space after the colon for vouchers --- common/vouchers.go | 4 ++-- common/vouchers_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/vouchers.go b/common/vouchers.go index 6cff91d..790e3ca 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -24,7 +24,7 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { var symbols, balances, decimals, addresses []string for i, h := range holdings { - symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) + symbols = append(symbols, fmt.Sprintf("%d: %s", i+1, h.TokenSymbol)) // Scale down the balance scaledBalance := ScaleDownBalance(h.Balance, h.TokenDecimals) @@ -103,7 +103,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, logg.Tracef("found", "symlist", symList, "syms", symbols, "input", input) for i, sym := range symList { - parts := strings.SplitN(sym, ":", 2) + parts := strings.SplitN(sym, ": ", 2) if input == parts[0] || strings.EqualFold(input, parts[1]) { symbol = parts[1] diff --git a/common/vouchers_test.go b/common/vouchers_test.go index ba6cd60..2ebb4a0 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -34,7 +34,7 @@ func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { } func TestMatchVoucher(t *testing.T) { - symbols := "1:SRF\n2:MILO" + symbols := "1: SRF\n2: MILO" balances := "1:100\n2:200" decimals := "1:6\n2:4" addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa" @@ -65,7 +65,7 @@ func TestProcessVouchers(t *testing.T) { } expectedResult := VoucherMetadata{ - Symbols: "1:SRF\n2:MILO", + Symbols: "1: SRF\n2: MILO", Balances: "1:100\n2:20000", Decimals: "1:6\n2:4", Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa", @@ -90,7 +90,7 @@ func TestGetVoucherData(t *testing.T) { // Test voucher data mockData := map[DataTyp][]byte{ - DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), + DATA_VOUCHER_SYMBOLS: []byte("1: SRF\n2: MILO"), DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), From 6f3b30e2fe22ef20df038d5e0e99b0400a613bbb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:31:17 +0300 Subject: [PATCH 254/332] Capitalize statement details and add a space after the colon --- internal/handlers/ussd/menuhandler.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c5356d0..f9d8728 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1895,12 +1895,12 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] value := strings.TrimSpace(values[i]) date := strings.Split(strings.TrimSpace(dates[i]), " ")[0] - status := "received" + status := "Received" if sender == string(publicKey) { - status = "sent" + status = "Sent" } - formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d:%s %s %s %s", i+1, status, value, sym, date)) + formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d: %s %s %s %s", i+1, status, value, sym, date)) } res.Content = strings.Join(formattedTransactions, "\n") From 1a4ee0d3e1f322f296321f124b490ec14824a53c Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:32:41 +0300 Subject: [PATCH 255/332] updated the description of the GetTransactionsList function --- internal/handlers/ussd/menuhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f9d8728..5723bad 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1846,7 +1846,7 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by return res, nil } -// GetTransactionsList fetches the list of transactions and formats them +// GetTransactionsList reads the list of transactions from the db and formats them func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From 2383e8ead3e9b97dc7d45f6667963a296b78edec Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:35:04 +0300 Subject: [PATCH 256/332] updated failed menuhandler_test --- internal/handlers/ussd/menuhandler_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 25470e8..5cc3067 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1961,7 +1961,7 @@ func TestCheckVouchers(t *testing.T) { {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } - expectedSym := []byte("1:SRF\n2:MILO") + expectedSym := []byte("1: SRF\n2: MILO") mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil) @@ -2024,7 +2024,7 @@ func TestViewVoucher(t *testing.T) { // Define mock voucher data mockData := map[common.DataTyp][]byte{ - common.DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), + common.DATA_VOUCHER_SYMBOLS: []byte("1: SRF\n2: MILO"), common.DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), common.DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), common.DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), From e05f8e72917fc013fab3494757d8b0777a71c7c2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 11 Dec 2024 19:46:52 +0300 Subject: [PATCH 257/332] update to the latest go-vise changes --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d89f0f3..d6f620f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a + git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede 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 diff --git a/go.sum b/go.sum index 5532c1c..e64d16f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE= git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede h1:BtooQZJDYEgeRKqv8RSxYSbW5jh0UIFJ6zRMEZZn0sc= +git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From c820e89cb751c3b7b6285c74afb18570282dc077 Mon Sep 17 00:00:00 2001 From: lash Date: Wed, 11 Dec 2024 19:13:13 +0000 Subject: [PATCH 258/332] Segment tx userdata types, add internals docs --- common/db.go | 15 +++++---------- doc/data.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 doc/data.md diff --git a/common/db.go b/common/db.go index f20dbab..a5cf1c1 100644 --- a/common/db.go +++ b/common/db.go @@ -19,20 +19,12 @@ import ( type DataTyp uint16 const ( - // TODO: Seems unused - DATA_ACCOUNT DataTyp = iota - // TODO: Seems unused, only read not written - DATA_ACCOUNT_CREATED // API Tracking id to follow status of account creation - DATA_TRACKING_ID + DATA_TRACKING_ID = iota // EVM address returned from API on account creation DATA_PUBLIC_KEY - // TODO: Seems unused - DATA_CUSTODIAL_ID // Currently active PIN used to authenticate ussd state change requests DATA_ACCOUNT_PIN - // TODO: Seems unused - DATA_ACCOUNT_STATUS // The first name of the user DATA_FIRST_NAME // The last name of the user @@ -75,7 +67,10 @@ const ( // List of voucher EVM addresses for vouchers valid in the user context. DATA_VOUCHER_ADDRESSES // List of senders for valid transactions in the user context. - DATA_TX_SENDERS +) + +const ( + DATA_TX_SENDERS = 512 + iota // List of recipients for valid transactions in the user context. DATA_TX_RECIPIENTS // List of voucher values for valid transactions in the user context. diff --git a/doc/data.md b/doc/data.md new file mode 100644 index 0000000..7a41f0c --- /dev/null +++ b/doc/data.md @@ -0,0 +1,28 @@ +# 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. From f8ea2daa736887e97733e24470ca9b4c7bdfe796 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 12 Dec 2024 19:55:01 +0300 Subject: [PATCH 259/332] initialize the restart state devtool --- devtools/restart_state/main.go | 110 +++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 devtools/restart_state/main.go diff --git a/devtools/restart_state/main.go b/devtools/restart_state/main.go new file mode 100644 index 0000000..136b38a --- /dev/null +++ b/devtools/restart_state/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "context" + "flag" + "fmt" + "os" + "path" + + "git.defalsify.org/vise.git/engine" + "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/resource" + + // "git.defalsify.org/vise.git/persist" + "git.grassecon.net/urdt/ussd/config" + "git.grassecon.net/urdt/ussd/initializers" + "git.grassecon.net/urdt/ussd/remote" + + "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/storage" +) + +var ( + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") +) + +func init() { + initializers.LoadEnvVariables() +} + +func main() { + config.LoadConfig() + + var dbDir string + var sessionId string + var database string + var engineDebug bool + var size uint + + flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") + flag.StringVar(&database, "db", "gdbm", "database to be used") + flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") + flag.BoolVar(&engineDebug, "d", false, "use engine debug output") + flag.Parse() + + ctx := context.Background() + ctx = context.WithValue(ctx, "SessionId", sessionId) + ctx = context.WithValue(ctx, "Database", database) + + pfp := path.Join(scriptDir, "pp.csv") + + cfg := engine.Config{ + Root: "root", + SessionId: sessionId, + OutputSize: uint32(size), + FlagCount: uint32(128), + } + + resourceDir := scriptDir + menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) + + err := menuStorageService.EnsureDbDir() + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + rs, err := menuStorageService.GetResource(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + pe, err := menuStorageService.GetPersister(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + userdatastore, err := menuStorageService.GetUserdataDb(ctx) + if err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } + + + // initialize the persister + + // get the state + + // restart the state + + // persist the state + + // exit + + st := pe.GetState() + + if st == nil { + logg.ErrorCtxf(ctx, "state fail in devtool", "state", st) + fmt.Errorf("cannot get state") + os.Exit(1) + } + + st.Restart() + + + os.Exit(1) +} From 0d7f7aaca139aed0a6d2cf0e19082bd847805870 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 12 Dec 2024 21:09:48 +0300 Subject: [PATCH 260/332] use latest commits from go-vise --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index d6f620f..246c57d 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede + git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf 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 diff --git a/go.sum b/go.sum index e64d16f..a21aa2f 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuR git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede h1:BtooQZJDYEgeRKqv8RSxYSbW5jh0UIFJ6zRMEZZn0sc= git.defalsify.org/vise.git v0.2.1-0.20241209194740-26fc82a76ede/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf h1:K58RcCiBZSmfU7zwg4nCHzY5ikw+wyfFoDU+4SXkarU= +git.defalsify.org/vise.git v0.2.1-0.20241212130612-18a95521a7cf/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From 6cc285d1e8e2184466cab608581cbc2be14be078 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 12 Dec 2024 21:12:25 +0300 Subject: [PATCH 261/332] add the custom separator to the menu --- cmd/africastalking/main.go | 9 ++++++--- cmd/async/main.go | 9 ++++++--- cmd/http/main.go | 9 ++++++--- cmd/main.go | 11 +++++++---- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 7d1caa8..f57f8f4 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -115,6 +115,7 @@ func main() { var engineDebug bool var host string var port uint + var separator string flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&database, "db", "gdbm", "database to be used") @@ -122,6 +123,7 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") + flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "build", build, "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) @@ -131,9 +133,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: separator, } if engineDebug { diff --git a/cmd/async/main.go b/cmd/async/main.go index e4c94b0..1fdc532 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -53,6 +53,7 @@ func main() { var engineDebug bool var host string var port uint + var separator string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") @@ -61,6 +62,7 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") + flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId) @@ -70,9 +72,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: separator, } if engineDebug { diff --git a/cmd/http/main.go b/cmd/http/main.go index 96e2688..aa7d00a 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -42,6 +42,7 @@ func main() { var engineDebug bool var host string var port uint + var separator string flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&database, "db", "gdbm", "database to be used") @@ -49,6 +50,7 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") + flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) @@ -58,9 +60,10 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: separator, } if engineDebug { diff --git a/cmd/main.go b/cmd/main.go index 9599eb7..aec6a14 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -34,11 +34,13 @@ func main() { var sessionId string var database string var engineDebug bool + var separator string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") + flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "outputsize", size) @@ -49,10 +51,11 @@ func main() { pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ - Root: "root", - SessionId: sessionId, - OutputSize: uint32(size), - FlagCount: uint32(128), + Root: "root", + SessionId: sessionId, + OutputSize: uint32(size), + FlagCount: uint32(128), + MenuSeparator: separator, } resourceDir := scriptDir From f38ea59569404c8fa967f6d78279a2227f421a16 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 13 Dec 2024 01:10:46 +0300 Subject: [PATCH 262/332] set the separator as a config and not an arg --- cmd/africastalking/main.go | 4 +--- cmd/async/main.go | 4 +--- cmd/http/main.go | 4 +--- cmd/main.go | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index f57f8f4..11d78b2 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -115,7 +115,6 @@ func main() { var engineDebug bool var host string var port uint - var separator string flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&database, "db", "gdbm", "database to be used") @@ -123,7 +122,6 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") - flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "build", build, "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) @@ -136,7 +134,7 @@ func main() { Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: separator, + MenuSeparator: ": ", } if engineDebug { diff --git a/cmd/async/main.go b/cmd/async/main.go index 1fdc532..a5cac92 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -53,7 +53,6 @@ func main() { var engineDebug bool var host string var port uint - var separator string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") @@ -62,7 +61,6 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") - flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId) @@ -75,7 +73,7 @@ func main() { Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: separator, + MenuSeparator: ": ", } if engineDebug { diff --git a/cmd/http/main.go b/cmd/http/main.go index aa7d00a..451d693 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -42,7 +42,6 @@ func main() { var engineDebug bool var host string var port uint - var separator string flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir") flag.StringVar(&database, "db", "gdbm", "database to be used") @@ -50,7 +49,6 @@ func main() { flag.UintVar(&size, "s", 160, "max size of output") flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host") flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port") - flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size) @@ -63,7 +61,7 @@ func main() { Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: separator, + MenuSeparator: ": ", } if engineDebug { diff --git a/cmd/main.go b/cmd/main.go index aec6a14..ddcf7a5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -34,13 +34,11 @@ func main() { var sessionId string var database string var engineDebug bool - var separator string flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.UintVar(&size, "s", 160, "max size of output") - flag.StringVar(&separator, "sep", ": ", "custom separator for the menu") flag.Parse() logg.Infof("start command", "dbdir", dbDir, "outputsize", size) @@ -55,7 +53,7 @@ func main() { SessionId: sessionId, OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: separator, + MenuSeparator: ": ", } resourceDir := scriptDir From 64c1fe5276775e160bd5fe4e85cee3fed5d6329a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 13 Dec 2024 11:38:10 +0300 Subject: [PATCH 263/332] set the separator as a var and add it to the context --- cmd/africastalking/main.go | 11 ++++++----- cmd/async/main.go | 4 +++- cmd/http/main.go | 4 +++- cmd/main.go | 8 +++++--- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 11d78b2..58025e0 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -30,10 +30,10 @@ import ( ) var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") - - build = "dev" + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") + build = "dev" + menuSeparator = ": " ) func init() { @@ -128,13 +128,14 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: ": ", + MenuSeparator: menuSeparator, } if engineDebug { diff --git a/cmd/async/main.go b/cmd/async/main.go index a5cac92..e63f42a 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -23,6 +23,7 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + menuSeparator = ": " ) func init() { @@ -67,13 +68,14 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: ": ", + MenuSeparator: menuSeparator, } if engineDebug { diff --git a/cmd/http/main.go b/cmd/http/main.go index 451d693..5196d5f 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -26,6 +26,7 @@ import ( var ( logg = logging.NewVanilla() scriptDir = path.Join("services", "registration") + menuSeparator = ": " ) func init() { @@ -55,13 +56,14 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ Root: "root", OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: ": ", + MenuSeparator: menuSeparator, } if engineDebug { diff --git a/cmd/main.go b/cmd/main.go index ddcf7a5..62d16a7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -18,8 +18,9 @@ import ( ) var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") + menuSeparator = ": " ) func init() { @@ -46,6 +47,7 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) + ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ @@ -53,7 +55,7 @@ func main() { SessionId: sessionId, OutputSize: uint32(size), FlagCount: uint32(128), - MenuSeparator: ": ", + MenuSeparator: menuSeparator, } resourceDir := scriptDir From df5e5f1a4bea91849564cf3963337800f5c473d1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 13 Dec 2024 11:40:39 +0300 Subject: [PATCH 264/332] properly format the vouchers --- common/vouchers.go | 4 ++-- common/vouchers_test.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/vouchers.go b/common/vouchers.go index 790e3ca..6cff91d 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -24,7 +24,7 @@ func ProcessVouchers(holdings []dataserviceapi.TokenHoldings) VoucherMetadata { var symbols, balances, decimals, addresses []string for i, h := range holdings { - symbols = append(symbols, fmt.Sprintf("%d: %s", i+1, h.TokenSymbol)) + symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol)) // Scale down the balance scaledBalance := ScaleDownBalance(h.Balance, h.TokenDecimals) @@ -103,7 +103,7 @@ func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, logg.Tracef("found", "symlist", symList, "syms", symbols, "input", input) for i, sym := range symList { - parts := strings.SplitN(sym, ": ", 2) + parts := strings.SplitN(sym, ":", 2) if input == parts[0] || strings.EqualFold(input, parts[1]) { symbol = parts[1] diff --git a/common/vouchers_test.go b/common/vouchers_test.go index 2ebb4a0..ba6cd60 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -34,7 +34,7 @@ func InitializeTestDb(t *testing.T) (context.Context, *UserDataStore) { } func TestMatchVoucher(t *testing.T) { - symbols := "1: SRF\n2: MILO" + symbols := "1:SRF\n2:MILO" balances := "1:100\n2:200" decimals := "1:6\n2:4" addresses := "1:0xd4c288865Ce\n2:0x41c188d63Qa" @@ -65,7 +65,7 @@ func TestProcessVouchers(t *testing.T) { } expectedResult := VoucherMetadata{ - Symbols: "1: SRF\n2: MILO", + Symbols: "1:SRF\n2:MILO", Balances: "1:100\n2:20000", Decimals: "1:6\n2:4", Addresses: "1:0xd4c288865Ce\n2:0x41c188d63Qa", @@ -90,7 +90,7 @@ func TestGetVoucherData(t *testing.T) { // Test voucher data mockData := map[DataTyp][]byte{ - DATA_VOUCHER_SYMBOLS: []byte("1: SRF\n2: MILO"), + DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), From 5cd791aae7f5900d046ce918b2cb61518720a363 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 13 Dec 2024 11:43:47 +0300 Subject: [PATCH 265/332] use the MenuSeparator --- internal/handlers/ussd/menuhandler.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 5723bad..d1d246a 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1676,6 +1676,11 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + menuSeparator, ok := ctx.Value("MenuSeparator").(string) + if !ok { + return res, fmt.Errorf("missing menu Separator") + } + // Read vouchers from the store voucherData, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS)) if err != nil { @@ -1683,7 +1688,9 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) return res, err } - res.Content = string(voucherData) + formattedData := strings.ReplaceAll(string(voucherData), ":", menuSeparator) + + res.Content = string(formattedData) return res, nil } @@ -1853,6 +1860,12 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] if !ok { return res, fmt.Errorf("missing session") } + + menuSeparator, ok := ctx.Value("MenuSeparator").(string) + if !ok { + return res, fmt.Errorf("missing menu Separator") + } + store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { @@ -1900,7 +1913,7 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] status = "Sent" } - formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d: %s %s %s %s", i+1, status, value, sym, date)) + formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d%s%s %s %s %s", i+1, menuSeparator, status, value, sym, date)) } res.Content = strings.Join(formattedTransactions, "\n") From 7aea2af9a1baca5ca5b9ef3ac38ef74f6b1b08d3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 13 Dec 2024 11:44:04 +0300 Subject: [PATCH 266/332] updated tests --- internal/handlers/ussd/menuhandler_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 5cc3067..01beb89 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1961,7 +1961,7 @@ func TestCheckVouchers(t *testing.T) { {ContractAddress: "0x41c188d63Qa", TokenSymbol: "MILO", TokenDecimals: "4", Balance: "200"}, } - expectedSym := []byte("1: SRF\n2: MILO") + expectedSym := []byte("1:SRF\n2:MILO") mockAccountService.On("FetchVouchers", string(publicKey)).Return(mockVouchersResponse, nil) @@ -1982,7 +1982,11 @@ func TestCheckVouchers(t *testing.T) { func TestGetVoucherList(t *testing.T) { sessionId := "session123" + menuSeparator := ":" + ctx := context.WithValue(context.Background(), "SessionId", sessionId) + ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) + spdb := InitializeTestSubPrefixDb(t, ctx) @@ -2024,7 +2028,7 @@ func TestViewVoucher(t *testing.T) { // Define mock voucher data mockData := map[common.DataTyp][]byte{ - common.DATA_VOUCHER_SYMBOLS: []byte("1: SRF\n2: MILO"), + common.DATA_VOUCHER_SYMBOLS: []byte("1:SRF\n2:MILO"), common.DATA_VOUCHER_BALANCES: []byte("1:100\n2:200"), common.DATA_VOUCHER_DECIMALS: []byte("1:6\n2:4"), common.DATA_VOUCHER_ADDRESSES: []byte("1:0xd4c288865Ce\n2:0x41c188d63Qa"), From 8d4d8a48e02367842b43fd5940bfbaf13a56a2ba Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 14 Dec 2024 11:56:31 +0000 Subject: [PATCH 267/332] Fix compile errors, test errors --- cmd/africastalking/main.go | 13 ++++++------- internal/handlers/ussd/menuhandler.go | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 7d1caa8..98864db 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -7,7 +7,6 @@ import ( "flag" "fmt" "io" - "log" "net/http" "os" "os/signal" @@ -45,14 +44,14 @@ type atRequestParser struct{} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { - log.Printf("got an invalid request:", rq) + logg.Warnf("got an invalid request", "req", rq) return "", handlers.ErrInvalidRequest } // Capture body (if any) for logging body, err := io.ReadAll(rqv.Body) if err != nil { - log.Printf("failed to read request body:", err) + logg.Warnf("failed to read request body", "err", err) return "", fmt.Errorf("failed to read request body: %v", err) } // Reset the body for further reading @@ -62,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { bodyLog := map[string]string{"body": string(body)} logBytes, err := json.Marshal(bodyLog) if err != nil { - log.Printf("failed to marshal request body:", err) + logg.Warnf("failed to marshal request body", "err", err) } else { - log.Printf("Received request:", string(logBytes)) + logg.Debugf("received request", "bytes", logBytes) } if err := rqv.ParseForm(); err != nil { - log.Printf("failed to parse form data: %v", err) + logg.Warnf("failed to parse form data", "err", err) return "", fmt.Errorf("failed to parse form data: %v", err) } @@ -79,7 +78,7 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { formattedNumber, err := common.FormatPhoneNumber(phoneNumber) if err != nil { - fmt.Printf("Error: %v\n", err) + logg.Warnf("failed to format phone number", "err", err) return "", fmt.Errorf("failed to format number") } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index b47e31f..dff01d1 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -213,7 +213,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, fmt.Errorf("missing session") } store := h.userdataStore - _, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED) + _, err = store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { if db.IsNotFound(err) { logg.InfoCtxf(ctx, "Creating an account because it doesn't exist") From 1ab49647f61f98bb1fd5c2b306ac53a27abf7e9e Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 14 Dec 2024 12:02:13 +0000 Subject: [PATCH 268/332] Upgrade vise --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index aeac055..e1b7ddb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5 + git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 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 diff --git a/go.sum b/go.sum index 5949d8b..ef7b782 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5 h1:QI/6xmpFM9NsG1+vRPzlUkkjAQ1Z8WhNqS+2tCjBpxg= -git.defalsify.org/vise.git v0.2.1-0.20241208224318-af1d15258dc5/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw= +git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From 549782f2309cb6acadb90698cf763514e099a940 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 17 Dec 2024 15:12:38 +0300 Subject: [PATCH 269/332] add link to terms page --- services/registration/terms | 3 ++- services/registration/terms_swa | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/services/registration/terms b/services/registration/terms index 05b8c11..2081d41 100644 --- a/services/registration/terms +++ b/services/registration/terms @@ -1 +1,2 @@ -Do you agree to terms and conditions? \ No newline at end of file +Do you agree to terms and conditions? +https://grassecon.org/pages/terms-and-conditions \ No newline at end of file diff --git a/services/registration/terms_swa b/services/registration/terms_swa index 7113cd7..cd6f945 100644 --- a/services/registration/terms_swa +++ b/services/registration/terms_swa @@ -1 +1,2 @@ -Kwa kutumia hii huduma umekubali sheria na masharti? \ No newline at end of file +Kwa kutumia hii huduma umekubali sheria na masharti? +https://grassecon.org/pages/terms-and-conditions \ No newline at end of file From fd6ff86579ab233921e534f6067eceb9067ebd52 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 17 Dec 2024 15:24:15 +0300 Subject: [PATCH 270/332] add link to terms and conditions as expected content --- menutraversal_test/test_setup.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 1f9d61d..caea216 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -11,7 +11,7 @@ }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No" + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n0:Yes\n1:No" }, { "input": "0", @@ -44,7 +44,7 @@ }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No" + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n0:Yes\n1:No" }, { "input": "1", From 70b2fa4ac2de1fa6893e9aabbd22e06ba0c649ec Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 17 Dec 2024 15:46:28 +0300 Subject: [PATCH 271/332] add spacing after link --- services/registration/terms | 2 +- services/registration/terms_swa | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/services/registration/terms b/services/registration/terms index 2081d41..8af5b06 100644 --- a/services/registration/terms +++ b/services/registration/terms @@ -1,2 +1,2 @@ Do you agree to terms and conditions? -https://grassecon.org/pages/terms-and-conditions \ No newline at end of file +https://grassecon.org/pages/terms-and-conditions diff --git a/services/registration/terms_swa b/services/registration/terms_swa index cd6f945..5678186 100644 --- a/services/registration/terms_swa +++ b/services/registration/terms_swa @@ -1,2 +1,2 @@ Kwa kutumia hii huduma umekubali sheria na masharti? -https://grassecon.org/pages/terms-and-conditions \ No newline at end of file +https://grassecon.org/pages/terms-and-conditions From 9b33117cb15957a97667e2efeda1a4f3a9ce89d3 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 17 Dec 2024 16:02:35 +0300 Subject: [PATCH 272/332] add space on expected content --- menutraversal_test/test_setup.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index caea216..c5860b4 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -11,7 +11,7 @@ }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n0:Yes\n1:No" + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No" }, { "input": "0", @@ -44,7 +44,7 @@ }, { "input": "0", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n0:Yes\n1:No" + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No" }, { "input": "1", From 5428626c3f6cef24196be7d391b20b794ec284b1 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 17 Dec 2024 17:56:56 +0300 Subject: [PATCH 273/332] cleaned up the restart_state --- devtools/restart_state/main.go | 35 +--------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/devtools/restart_state/main.go b/devtools/restart_state/main.go index 136b38a..3068a38 100644 --- a/devtools/restart_state/main.go +++ b/devtools/restart_state/main.go @@ -7,16 +7,9 @@ import ( "os" "path" - "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/logging" - "git.defalsify.org/vise.git/resource" - - // "git.defalsify.org/vise.git/persist" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" - "git.grassecon.net/urdt/ussd/remote" - - "git.grassecon.net/urdt/ussd/internal/handlers" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -35,28 +28,16 @@ func main() { var dbDir string var sessionId string var database string - var engineDebug bool - var size uint - + flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") flag.StringVar(&database, "db", "gdbm", "database to be used") flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") - flag.BoolVar(&engineDebug, "d", false, "use engine debug output") flag.Parse() ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) - pfp := path.Join(scriptDir, "pp.csv") - - cfg := engine.Config{ - Root: "root", - SessionId: sessionId, - OutputSize: uint32(size), - FlagCount: uint32(128), - } - resourceDir := scriptDir menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) @@ -66,25 +47,12 @@ func main() { os.Exit(1) } - rs, err := menuStorageService.GetResource(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - pe, err := menuStorageService.GetPersister(ctx) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } - userdatastore, err := menuStorageService.GetUserdataDb(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - - // initialize the persister // get the state @@ -105,6 +73,5 @@ func main() { st.Restart() - os.Exit(1) } From 553470618913fca61232761e3ffcfa8efe00cbab Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 17 Dec 2024 17:58:08 +0300 Subject: [PATCH 274/332] reset the state when input is nil --- internal/handlers/ussd/menuhandler.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c5356d0..c2b41b2 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -124,6 +124,16 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource h.st = h.pe.GetState() h.ca = h.pe.GetMemory() + + fmt.Println("Pre restart state:", h.st) + + if len(input) == 0 { + h.st.Restart() + h.st = h.pe.GetState() + } + + fmt.Println("New state:", h.st) + sessionId, _ := ctx.Value("SessionId").(string) flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") From 17ba6a06bacdc1398095fd9a2ea0e72094e097f9 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 18 Dec 2024 21:49:42 +0300 Subject: [PATCH 275/332] remove the MenuSeparator from the context --- cmd/africastalking/main.go | 1 - cmd/async/main.go | 1 - cmd/http/main.go | 1 - cmd/main.go | 1 - 4 files changed, 4 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 58025e0..3d11ba5 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -128,7 +128,6 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/async/main.go b/cmd/async/main.go index e63f42a..9cd04b3 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -68,7 +68,6 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/http/main.go b/cmd/http/main.go index 5196d5f..6ddfded 100644 --- a/cmd/http/main.go +++ b/cmd/http/main.go @@ -56,7 +56,6 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ diff --git a/cmd/main.go b/cmd/main.go index 62d16a7..4fd084f 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -47,7 +47,6 @@ func main() { ctx := context.Background() ctx = context.WithValue(ctx, "SessionId", sessionId) ctx = context.WithValue(ctx, "Database", database) - ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) pfp := path.Join(scriptDir, "pp.csv") cfg := engine.Config{ From d08afff44395133495873cec2ea07b55b550524c Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 18 Dec 2024 21:56:37 +0300 Subject: [PATCH 276/332] add the replaceSeparator func and pass it to the Handler struct --- internal/handlers/handlerservice.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index a14cf59..417fe18 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -2,6 +2,7 @@ package handlers import ( "context" + "strings" "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/db" @@ -64,7 +65,11 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { } func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService) + replaceSeparator := func(input string) string { + return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator) + } + + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparator) if err != nil { return nil, err } From fda68231eacefcdb20edab828f3903b1fc8642c3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 18 Dec 2024 21:59:09 +0300 Subject: [PATCH 277/332] use the replaceSeparator func to format the generated menus --- internal/handlers/ussd/menuhandler.go | 50 ++++++++++++--------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d1d246a..7b238e3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -69,18 +69,19 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { } type Handlers struct { - pe *persist.Persister - st *state.State - ca cache.Memory - userdataStore common.DataStore - adminstore *utils.AdminStore - flagManager *asm.FlagParser - accountService remote.AccountServiceInterface - prefixDb storage.PrefixDb - profile *models.Profile + pe *persist.Persister + st *state.State + ca cache.Memory + userdataStore common.DataStore + adminstore *utils.AdminStore + flagManager *asm.FlagParser + accountService remote.AccountServiceInterface + prefixDb storage.PrefixDb + profile *models.Profile + ReplaceSeparator func(string) string } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface, replaceSeparator func(string) string) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } @@ -93,12 +94,13 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ - userdataStore: userDb, - flagManager: appFlags, - adminstore: adminstore, - accountService: accountService, - prefixDb: prefixDb, - profile: &models.Profile{Max: 6}, + userdataStore: userDb, + flagManager: appFlags, + adminstore: adminstore, + accountService: accountService, + prefixDb: prefixDb, + profile: &models.Profile{Max: 6}, + ReplaceSeparator: replaceSeparator, } return h, nil } @@ -1676,11 +1678,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result - menuSeparator, ok := ctx.Value("MenuSeparator").(string) - if !ok { - return res, fmt.Errorf("missing menu Separator") - } - // Read vouchers from the store voucherData, err := h.prefixDb.Get(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS)) if err != nil { @@ -1688,7 +1685,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) return res, err } - formattedData := strings.ReplaceAll(string(voucherData), ":", menuSeparator) + formattedData := h.ReplaceSeparator(string(voucherData)) res.Content = string(formattedData) @@ -1861,11 +1858,6 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] return res, fmt.Errorf("missing session") } - menuSeparator, ok := ctx.Value("MenuSeparator").(string) - if !ok { - return res, fmt.Errorf("missing menu Separator") - } - store := h.userdataStore publicKey, err := store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY) if err != nil { @@ -1913,7 +1905,9 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] status = "Sent" } - formattedTransactions = append(formattedTransactions, fmt.Sprintf("%d%s%s %s %s %s", i+1, menuSeparator, status, value, sym, date)) + // Use the ReplaceSeparator function for the menu separator + transactionLine := fmt.Sprintf("%d%s%s %s %s %s", i+1, h.ReplaceSeparator(":"), status, value, sym, date) + formattedTransactions = append(formattedTransactions, transactionLine) } res.Content = strings.Join(formattedTransactions, "\n") From ecfdab47a8cc0f12e0b4cea6889069421a1adf21 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 18 Dec 2024 22:21:52 +0300 Subject: [PATCH 278/332] updated test --- internal/handlers/ussd/menuhandler_test.go | 58 ++++++++++++++++------ 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 01beb89..22c02e9 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "path" + "strings" "testing" "git.defalsify.org/vise.git/lang" @@ -67,12 +68,20 @@ func TestNewHandlers(t *testing.T) { _, store := InitializeTestStore(t) fm, err := NewFlagManager(flagsPath) - accountService := testservice.TestAccountService{} if err != nil { - t.Logf(err.Error()) + log.Fatal(err) } + + accountService := testservice.TestAccountService{} + + // Mock function for replaceSeparator + mockReplaceSeparator := func(input string) string { + return strings.ReplaceAll(input, ":", ": ") + } + + // Test case for valid UserDataStore t.Run("Valid UserDataStore", func(t *testing.T) { - handlers, err := NewHandlers(fm.parser, store, nil, &accountService) + handlers, err := NewHandlers(fm.parser, store, nil, &accountService, mockReplaceSeparator) if err != nil { t.Fatalf("expected no error, got %v", err) } @@ -82,19 +91,30 @@ func TestNewHandlers(t *testing.T) { if handlers.userdataStore == nil { t.Fatal("expected userdataStore to be set in handlers") } + if handlers.ReplaceSeparator == nil { + t.Fatal("expected ReplaceSeparator to be set in handlers") + } + + // Test ReplaceSeparator functionality + input := "1:Menu item" + expectedOutput := "1: Menu item" + if handlers.ReplaceSeparator(input) != expectedOutput { + t.Fatalf("ReplaceSeparator function did not return expected output: got %v, want %v", handlers.ReplaceSeparator(input), expectedOutput) + } }) - // Test case for nil userdataStore + // Test case for nil UserDataStore t.Run("Nil UserDataStore", func(t *testing.T) { - handlers, err := NewHandlers(fm.parser, nil, nil, &accountService) + handlers, err := NewHandlers(fm.parser, nil, nil, &accountService, mockReplaceSeparator) if err == nil { t.Fatal("expected an error, got none") } if handlers != nil { t.Fatal("expected handlers to be nil") } - if err.Error() != "cannot create handler with nil userdata store" { - t.Fatalf("expected specific error, got %v", err) + expectedError := "cannot create handler with nil userdata store" + if err.Error() != expectedError { + t.Fatalf("expected error '%s', got '%v'", expectedError, err) } }) } @@ -1982,30 +2002,36 @@ func TestCheckVouchers(t *testing.T) { func TestGetVoucherList(t *testing.T) { sessionId := "session123" - menuSeparator := ":" - + ctx := context.WithValue(context.Background(), "SessionId", sessionId) - ctx = context.WithValue(ctx, "MenuSeparator", menuSeparator) - spdb := InitializeTestSubPrefixDb(t, ctx) - h := &Handlers{ - prefixDb: spdb, + // Mock replaceSeparator function + replaceSeparator := func(input string) string { + return strings.ReplaceAll(input, ":", ": ") } - expectedSym := []byte("1:SRF\n2:MILO") + // Initialize Handlers + h := &Handlers{ + prefixDb: spdb, + ReplaceSeparator: replaceSeparator, + } + + mockSyms := []byte("1:SRF\n2:MILO") // Put voucher sym data from the store - err := spdb.Put(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS), expectedSym) + err := spdb.Put(ctx, common.ToBytes(common.DATA_VOUCHER_SYMBOLS), mockSyms) if err != nil { t.Fatal(err) } + expectedSyms := []byte("1: SRF\n2: MILO") + res, err := h.GetVoucherList(ctx, "", []byte("")) assert.NoError(t, err) - assert.Equal(t, res.Content, string(expectedSym)) + assert.Equal(t, res.Content, string(expectedSyms)) } func TestViewVoucher(t *testing.T) { From 055c2db790281cfc9517f7aa878c32f2329b5670 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 18 Dec 2024 22:25:47 +0300 Subject: [PATCH 279/332] use a common mockReplaceSeparator func --- internal/handlers/ussd/menuhandler_test.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 22c02e9..3c32ee0 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -33,6 +33,11 @@ var ( flagsPath = path.Join(baseDir, "services", "registration", "pp.csv") ) +// mockReplaceSeparator function +var mockReplaceSeparator = func(input string) string { + return strings.ReplaceAll(input, ":", ": ") +} + // InitializeTestStore sets up and returns an in-memory database and store. func InitializeTestStore(t *testing.T) (context.Context, *common.UserDataStore) { ctx := context.Background() @@ -74,11 +79,6 @@ func TestNewHandlers(t *testing.T) { accountService := testservice.TestAccountService{} - // Mock function for replaceSeparator - mockReplaceSeparator := func(input string) string { - return strings.ReplaceAll(input, ":", ": ") - } - // Test case for valid UserDataStore t.Run("Valid UserDataStore", func(t *testing.T) { handlers, err := NewHandlers(fm.parser, store, nil, &accountService, mockReplaceSeparator) @@ -2007,15 +2007,10 @@ func TestGetVoucherList(t *testing.T) { spdb := InitializeTestSubPrefixDb(t, ctx) - // Mock replaceSeparator function - replaceSeparator := func(input string) string { - return strings.ReplaceAll(input, ":", ": ") - } - // Initialize Handlers h := &Handlers{ prefixDb: spdb, - ReplaceSeparator: replaceSeparator, + ReplaceSeparator: mockReplaceSeparator, } mockSyms := []byte("1:SRF\n2:MILO") From 12928512264e6144c20169bcde51fed7c704eece Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 19 Dec 2024 13:32:39 +0300 Subject: [PATCH 280/332] rename the function to ReplaceSeparatorFunc --- internal/handlers/handlerservice.go | 4 ++-- internal/handlers/ussd/menuhandler.go | 10 +++++----- internal/handlers/ussd/menuhandler_test.go | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 417fe18..015098e 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -65,11 +65,11 @@ func (ls *LocalHandlerService) SetDataStore(db *db.Db) { } func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceInterface) (*ussd.Handlers, error) { - replaceSeparator := func(input string) string { + replaceSeparatorFunc := func(input string) string { return strings.ReplaceAll(input, ":", ls.Cfg.MenuSeparator) } - ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparator) + ussdHandlers, err := ussd.NewHandlers(ls.Parser, *ls.UserdataStore, ls.AdminStore, accountService, replaceSeparatorFunc) if err != nil { return nil, err } diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index f506793..986f8f0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -78,10 +78,10 @@ type Handlers struct { accountService remote.AccountServiceInterface prefixDb storage.PrefixDb profile *models.Profile - ReplaceSeparator func(string) string + ReplaceSeparatorFunc func(string) string } -func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface, replaceSeparator func(string) string) (*Handlers, error) { +func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface, replaceSeparatorFunc func(string) string) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") } @@ -100,7 +100,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util accountService: accountService, prefixDb: prefixDb, profile: &models.Profile{Max: 6}, - ReplaceSeparator: replaceSeparator, + ReplaceSeparatorFunc: replaceSeparatorFunc, } return h, nil } @@ -1685,7 +1685,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) return res, err } - formattedData := h.ReplaceSeparator(string(voucherData)) + formattedData := h.ReplaceSeparatorFunc(string(voucherData)) res.Content = string(formattedData) @@ -1906,7 +1906,7 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] } // Use the ReplaceSeparator function for the menu separator - transactionLine := fmt.Sprintf("%d%s%s %s %s %s", i+1, h.ReplaceSeparator(":"), status, value, sym, date) + transactionLine := fmt.Sprintf("%d%s%s %s %s %s", i+1, h.ReplaceSeparatorFunc(":"), status, value, sym, date) formattedTransactions = append(formattedTransactions, transactionLine) } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 3c32ee0..c01678d 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -91,15 +91,15 @@ func TestNewHandlers(t *testing.T) { if handlers.userdataStore == nil { t.Fatal("expected userdataStore to be set in handlers") } - if handlers.ReplaceSeparator == nil { - t.Fatal("expected ReplaceSeparator to be set in handlers") + if handlers.ReplaceSeparatorFunc == nil { + t.Fatal("expected ReplaceSeparatorFunc to be set in handlers") } - // Test ReplaceSeparator functionality + // Test ReplaceSeparatorFunc functionality input := "1:Menu item" expectedOutput := "1: Menu item" - if handlers.ReplaceSeparator(input) != expectedOutput { - t.Fatalf("ReplaceSeparator function did not return expected output: got %v, want %v", handlers.ReplaceSeparator(input), expectedOutput) + if handlers.ReplaceSeparatorFunc(input) != expectedOutput { + t.Fatalf("ReplaceSeparatorFunc function did not return expected output: got %v, want %v", handlers.ReplaceSeparatorFunc(input), expectedOutput) } }) @@ -2009,8 +2009,8 @@ func TestGetVoucherList(t *testing.T) { // Initialize Handlers h := &Handlers{ - prefixDb: spdb, - ReplaceSeparator: mockReplaceSeparator, + prefixDb: spdb, + ReplaceSeparatorFunc: mockReplaceSeparator, } mockSyms := []byte("1:SRF\n2:MILO") From be215d3f75ed64a1794e87497a3682b5050842b7 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 20 Dec 2024 12:26:07 +0300 Subject: [PATCH 281/332] set the code to an empty byte for it to move to the top node --- internal/handlers/ussd/menuhandler.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index b3c6f1d..f509ff4 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -124,16 +124,11 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource h.st = h.pe.GetState() h.ca = h.pe.GetMemory() - - fmt.Println("Pre restart state:", h.st) - if len(input) == 0 { - h.st.Restart() - h.st = h.pe.GetState() + // move to the top node + h.st.Code = []byte{} } - fmt.Println("New state:", h.st) - sessionId, _ := ctx.Value("SessionId").(string) flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") From f4f4fdd3aceac3c170c71d5ef317afdee7fa7447 Mon Sep 17 00:00:00 2001 From: konstantinmds Date: Tue, 17 Dec 2024 16:32:10 +0100 Subject: [PATCH 282/332] issue-205: added comments for menu handlers methods and changed function name to better fit function workings. --- internal/handlers/handlerservice.go | 2 +- internal/handlers/ussd/menuhandler.go | 125 ++++++++++++++++---------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 015098e..1da28c3 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -116,7 +116,7 @@ func (ls *LocalHandlerService) GetHandler(accountService remote.AccountServiceIn ls.DbRs.AddLocalFunc("set_voucher", ussdHandlers.SetVoucher) ls.DbRs.AddLocalFunc("get_voucher_details", ussdHandlers.GetVoucherDetails) ls.DbRs.AddLocalFunc("reset_valid_pin", ussdHandlers.ResetValidPin) - ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckPinMisMatch) + ls.DbRs.AddLocalFunc("check_pin_mismatch", ussdHandlers.CheckBlockedNumPinMisMatch) ls.DbRs.AddLocalFunc("validate_blocked_number", ussdHandlers.ValidateBlockedNumber) ls.DbRs.AddLocalFunc("retrieve_blocked_number", ussdHandlers.RetrieveBlockedNumber) ls.DbRs.AddLocalFunc("reset_unregistered_number", ussdHandlers.ResetUnregisteredNumber) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 986f8f0..daf4b29 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -39,7 +39,7 @@ const ( pinPattern = `^\d{4}$` ) -// isValidPIN checks whether the given input is a 4 digit number +// checks whether the given input is a 4 digit number func isValidPIN(pin string) bool { match, _ := regexp.MatchString(pinPattern, pin) return match @@ -69,18 +69,19 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { } type Handlers struct { - pe *persist.Persister - st *state.State - ca cache.Memory - userdataStore common.DataStore - adminstore *utils.AdminStore - flagManager *asm.FlagParser - accountService remote.AccountServiceInterface - prefixDb storage.PrefixDb - profile *models.Profile + pe *persist.Persister + st *state.State + ca cache.Memory + userdataStore common.DataStore + adminstore *utils.AdminStore + flagManager *asm.FlagParser + accountService remote.AccountServiceInterface + prefixDb storage.PrefixDb + profile *models.Profile ReplaceSeparatorFunc func(string) string } +// NewHandlers creates a new instance of the Handlers struct with the provided dependencies. func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *utils.AdminStore, accountService remote.AccountServiceInterface, replaceSeparatorFunc func(string) string) (*Handlers, error) { if userdataStore == nil { return nil, fmt.Errorf("cannot create handler with nil userdata store") @@ -94,17 +95,18 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ - userdataStore: userDb, - flagManager: appFlags, - adminstore: adminstore, - accountService: accountService, - prefixDb: prefixDb, - profile: &models.Profile{Max: 6}, + userdataStore: userDb, + flagManager: appFlags, + adminstore: adminstore, + accountService: accountService, + prefixDb: prefixDb, + profile: &models.Profile{Max: 6}, ReplaceSeparatorFunc: replaceSeparatorFunc, } return h, nil } +// WithPersister sets persister instance to the handlers. func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { if h.pe != nil { panic("persister already set") @@ -113,6 +115,7 @@ func (h *Handlers) WithPersister(pe *persist.Persister) *Handlers { return h } +// Init initializes the handler for a new session. func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource.Result, error) { var r resource.Result if h.pe == nil { @@ -151,7 +154,7 @@ func (h *Handlers) Exit() { h.pe = nil } -// SetLanguage sets the language across the menu +// SetLanguage sets the language across the menu. func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -175,6 +178,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r return res, nil } +// handles the account creation when no existing account is present for the session and stores associated data in the user data store. func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, res *resource.Result) error { flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") r, err := h.accountService.CreateAccount(ctx) @@ -207,9 +211,9 @@ func (h *Handlers) createAccountNoExist(ctx context.Context, sessionId string, r return nil } -// CreateAccount checks if any account exists on the JSON data file, and if not +// CreateAccount checks if any account exists on the JSON data file, and if not, // creates an account on the API, -// sets the default values and flags +// sets the default values and flags. func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -233,19 +237,22 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, nil } -func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) { +// CheckBlockedNumPinMisMatch checks if the provided PIN matches a temporary PIN stored for a blocked number. +func (h *Handlers) CheckBlockedNumPinMisMatch(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") sessionId, ok := ctx.Value("SessionId").(string) if !ok { return res, fmt.Errorf("missing session") } + // Get blocked number from storage. store := h.userdataStore blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } + // Get temporary PIN for the blocked number. temporaryPin, err := store.ReadEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE) if err != nil { logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) @@ -259,6 +266,7 @@ func (h *Handlers) CheckPinMisMatch(ctx context.Context, sym string, input []byt return res, nil } +// VerifyNewPin checks if a new PIN meets the required format criteria. func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} _, ok := ctx.Value("SessionId").(string) @@ -267,7 +275,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) ( } flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") pinInput := string(input) - // Validate that the PIN is a 4-digit number + // Validate that the PIN is a 4-digit number. if isValidPIN(pinInput) { res.FlagSet = append(res.FlagSet, flag_valid_pin) } else { @@ -277,9 +285,9 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) ( return res, nil } -// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE +// SaveTemporaryPin saves the valid PIN input to the DATA_TEMPORARY_VALUE, // during the account creation process -// and during the change PIN process +// and during the change PIN process. func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -292,7 +300,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") accountPIN := string(input) - // Validate that the PIN is a 4-digit number + // Validate that the PIN is a 4-digit number. if !isValidPIN(accountPIN) { res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil @@ -308,6 +316,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt return res, nil } +// SaveOthersTemporaryPin allows authorized users to set temporary PINs for blocked numbers. func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -318,12 +327,14 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input return res, fmt.Errorf("missing session") } temporaryPin := string(input) + // First, we retrieve the blocked number associated with this session blockedNumber, err := store.ReadEntry(ctx, sessionId, common.DATA_BLOCKED_NUMBER) if err != nil { logg.ErrorCtxf(ctx, "failed to read blockedNumber entry with", "key", common.DATA_BLOCKED_NUMBER, "error", err) return res, err } + // Then we save the temporary PIN for that blocked number err = store.WriteEntry(ctx, string(blockedNumber), common.DATA_TEMPORARY_VALUE, []byte(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "value", temporaryPin, "error", err) @@ -333,6 +344,7 @@ func (h *Handlers) SaveOthersTemporaryPin(ctx context.Context, sym string, input return res, nil } +// ConfirmPinChange validates user's new PIN. If input matches the temporary PIN, saves it as the new account PIN. func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -352,6 +364,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt } else { res.FlagSet = append(res.FlagSet, flag_pin_mismatch) } + // If matched, save the confirmed PIN as the new account PIN err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) @@ -362,7 +375,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt // VerifyCreatePin checks whether the confirmation PIN is similar to the temporary PIN // If similar, it sets the USERFLAG_PIN_SET flag and writes the account PIN allowing the user -// to access the main menu +// to access the main menu. func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -397,7 +410,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte return res, nil } -// codeFromCtx retrieves language codes from the context that can be used for handling translations +// retrieves language codes from the context that can be used for handling translations. func codeFromCtx(ctx context.Context) string { var code string if ctx.Value("Language") != nil { @@ -731,7 +744,7 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by return res, nil } -// Setback sets the flag_back_set flag when the navigation is back +// Setback sets the flag_back_set flag when the navigation is back. func (h *Handlers) SetBack(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result //TODO: @@ -744,7 +757,7 @@ func (h *Handlers) SetBack(ctx context.Context, sym string, input []byte) (resou } // CheckAccountStatus queries the API using the TrackingId and sets flags -// based on the account status +// based on the account status. func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -784,7 +797,7 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b return res, nil } -// Quit displays the Thank you message and exits the menu +// Quit displays the Thank you message and exits the menu. func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -799,7 +812,7 @@ func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource return res, nil } -// QuitWithHelp displays helpline information then exits the menu +// QuitWithHelp displays helpline information then exits the menu. func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -814,7 +827,7 @@ func (h *Handlers) QuitWithHelp(ctx context.Context, sym string, input []byte) ( return res, nil } -// VerifyYob verifies the length of the given input +// VerifyYob verifies the length of the given input. func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -836,7 +849,7 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res return res, nil } -// ResetIncorrectYob resets the incorrect date format flag 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) { var res resource.Result @@ -846,7 +859,7 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by } // CheckBalance retrieves the balance of the active voucher and sets -// the balance as the result content +// the balance as the result content. func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -896,9 +909,12 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( return res, nil } +// FetchCommunityBalance retrieves and displays the balance for community accounts in user's preferred language. func (h *Handlers) FetchCommunityBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result + // retrieve the language code from the context code := codeFromCtx(ctx) + // Initialize the localization system with the appropriate translation directory l := gotext.NewLocale(translationDir, code) l.AddDomain("default") //TODO: @@ -907,6 +923,10 @@ func (h *Handlers) FetchCommunityBalance(ctx context.Context, sym string, input return res, nil } +// ResetOthersPin handles the PIN reset process for other users' accounts by: +// 1. Retrieving the blocked phone number from the session +// 2. Fetching the temporary PIN associated with that number +// 3. Updating the account PIN with the temporary PIN func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result store := h.userdataStore @@ -932,6 +952,8 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) return res, nil } +// ResetUnregisteredNumber clears the unregistered number flag in the system, +// indicating that a number's registration status should no longer be marked as unregistered. func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result flag_unregistered_number, _ := h.flagManager.GetFlag("flag_unregistered_number") @@ -939,6 +961,8 @@ func (h *Handlers) ResetUnregisteredNumber(ctx context.Context, sym string, inpu return res, nil } +// ValidateBlockedNumber performs validation of phone numbers, specifically for blocked numbers in the system. +// It checks phone number format and verifies registration status. func (h *Handlers) ValidateBlockedNumber(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1067,7 +1091,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by } // TransactionReset resets the previous transaction data (Recipient and Amount) -// as well as the invalid flags +// as well as the invalid flags. func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1120,7 +1144,7 @@ func (h *Handlers) InviteValidRecipient(ctx context.Context, sym string, input [ return res, nil } -// ResetTransactionAmount resets the transaction amount and invalid flag +// ResetTransactionAmount resets the transaction amount and invalid flag. func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1250,7 +1274,7 @@ func (h *Handlers) RetrieveBlockedNumber(ctx context.Context, sym string, input return res, nil } -// GetSender returns the sessionId (phoneNumber) +// GetSender returns the sessionId (phoneNumber). func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1264,7 +1288,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res return res, nil } -// GetAmount retrieves the amount from teh Gdbm Db +// GetAmount retrieves the amount from teh Gdbm Db. func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1288,7 +1312,7 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res return res, nil } -// InitiateTransaction calls the TokenTransfer and returns a confirmation based on the result +// InitiateTransaction calls the TokenTransfer and returns a confirmation based on the result. func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1338,6 +1362,8 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, nil } +// GetCurrentProfileInfo retrieves specific profile fields based on the current state of the USSD session. +// Uses flag management system to track profile field status and handle menu navigation. func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var profileInfo []byte @@ -1357,6 +1383,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input if !ok { return res, fmt.Errorf("missing session") } + // Extract the field name from the state machine position. sm, _ := h.st.Where() parts := strings.SplitN(sm, "_", 2) filename := parts[1] @@ -1449,6 +1476,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input return res, nil } +// GetProfileInfo provides a comprehensive view of a user's profile. func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var defaultValue string @@ -1517,7 +1545,7 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) } // SetDefaultVoucher retrieves the current vouchers -// and sets the first as the default voucher, if no active voucher is set +// and sets the first as the default voucher, if no active voucher is set. func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var err error @@ -1602,7 +1630,7 @@ func (h *Handlers) SetDefaultVoucher(ctx context.Context, sym string, input []by } // CheckVouchers retrieves the token holdings from the API using the "PublicKey" and stores -// them to gdbm +// them to gdbm. func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1674,7 +1702,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte) return res, nil } -// GetVoucherList fetches the list of vouchers and formats them +// GetVoucherList fetches the list of vouchers and formats them. func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1693,7 +1721,7 @@ func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) } // ViewVoucher retrieves the token holding and balance from the subprefixDB -// and displays it to the user for them to select it +// and displays it to the user for them to select it. func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1734,7 +1762,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r return res, nil } -// SetVoucher retrieves the temp voucher data and sets it as the active data +// SetVoucher retrieves the temp voucher data and sets it as the active data. func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result @@ -1760,7 +1788,7 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re return res, nil } -// GetVoucherDetails retrieves the voucher details +// GetVoucherDetails retrieves the voucher details. func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result store := h.userdataStore @@ -1792,7 +1820,7 @@ func (h *Handlers) GetVoucherDetails(ctx context.Context, sym string, input []by return res, nil } -// CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb +// CheckTransactions retrieves the transactions from the API using the "PublicKey" and stores to prefixDb. func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1850,7 +1878,7 @@ func (h *Handlers) CheckTransactions(ctx context.Context, sym string, input []by return res, nil } -// GetTransactionsList reads the list of transactions from the db and formats them +// GetTransactionsList fetches the list of transactions and formats them. func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1916,7 +1944,7 @@ func (h *Handlers) GetTransactionsList(ctx context.Context, sym string, input [] } // ViewTransactionStatement retrieves the transaction statement -// and displays it to the user +// and displays it to the user. func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) @@ -1964,6 +1992,7 @@ func (h *Handlers) ViewTransactionStatement(ctx context.Context, sym string, inp return res, nil } +// handles bulk updates of profile information. func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res *resource.Result) error { var err error store := h.userdataStore @@ -2000,7 +2029,7 @@ func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res return nil } -// UpdateAllProfileItems is used to persist all the new profile information and setup the required profile flags +// UpdateAllProfileItems is used to persist all the new profile information and setup the required profile flags. func (h *Handlers) UpdateAllProfileItems(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result sessionId, ok := ctx.Value("SessionId").(string) From 5579991d6644c10f7aae014f1c9ddf3127138c90 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 27 Dec 2024 10:07:05 +0300 Subject: [PATCH 283/332] guard profile update after being set --- internal/handlers/ussd/menuhandler.go | 47 ++++++++++++++------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 986f8f0..901f9b3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -69,15 +69,15 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { } type Handlers struct { - pe *persist.Persister - st *state.State - ca cache.Memory - userdataStore common.DataStore - adminstore *utils.AdminStore - flagManager *asm.FlagParser - accountService remote.AccountServiceInterface - prefixDb storage.PrefixDb - profile *models.Profile + pe *persist.Persister + st *state.State + ca cache.Memory + userdataStore common.DataStore + adminstore *utils.AdminStore + flagManager *asm.FlagParser + accountService remote.AccountServiceInterface + prefixDb storage.PrefixDb + profile *models.Profile ReplaceSeparatorFunc func(string) string } @@ -94,12 +94,12 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ - userdataStore: userDb, - flagManager: appFlags, - adminstore: adminstore, - accountService: accountService, - prefixDb: prefixDb, - profile: &models.Profile{Max: 6}, + userdataStore: userDb, + flagManager: appFlags, + adminstore: adminstore, + accountService: accountService, + prefixDb: prefixDb, + profile: &models.Profile{Max: 6}, ReplaceSeparatorFunc: replaceSeparatorFunc, } return h, nil @@ -1986,15 +1986,16 @@ func (h *Handlers) insertProfileItems(ctx context.Context, sessionId string, res for index, profileItem := range h.profile.ProfileItems { // Ensure the profileItem is not "0"(is set) if profileItem != "0" { - err = store.WriteEntry(ctx, sessionId, profileDataKeys[index], []byte(profileItem)) - if err != nil { - logg.ErrorCtxf(ctx, "failed to write profile entry with", "key", profileDataKeys[index], "value", profileItem, "error", err) - return err - } - - // Get the flag for the current index flag, _ := h.flagManager.GetFlag(profileFlagNames[index]) - res.FlagSet = append(res.FlagSet, flag) + isProfileItemSet := h.st.MatchFlag(flag, true) + if !isProfileItemSet { + err = store.WriteEntry(ctx, sessionId, profileDataKeys[index], []byte(profileItem)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write profile entry with", "key", profileDataKeys[index], "value", profileItem, "error", err) + return err + } + res.FlagSet = append(res.FlagSet, flag) + } } } return nil From 11eb61ba3524d6f3ee25e2e962a274986c1e58ce Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 27 Dec 2024 16:11:09 +0300 Subject: [PATCH 284/332] repeat same node on invalid selection --- services/registration/select_gender.vis | 1 + 1 file changed, 1 insertion(+) diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index c1a00f5..e41da10 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -11,3 +11,4 @@ INCMP _ 0 INCMP set_male 1 INCMP set_female 2 INCMP set_unspecified 3 +INCMP . * From 46edf2b819a250043d2c7fa99deafd6c34bcbd3c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 27 Dec 2024 16:13:36 +0300 Subject: [PATCH 285/332] remove redundant catch on pin entry --- services/registration/edit_offerings.vis | 1 - 1 file changed, 1 deletion(-) diff --git a/services/registration/edit_offerings.vis b/services/registration/edit_offerings.vis index ddbc9e0..9c1e747 100644 --- a/services/registration/edit_offerings.vis +++ b/services/registration/edit_offerings.vis @@ -10,5 +10,4 @@ CATCH _ flag_back_set 1 RELOAD save_offerings INCMP _ 0 CATCH pin_entry flag_offerings_set 1 -CATCH pin_entry flag_offerings_set 0 INCMP update_profile_items * From 58a60f2c81efa3c5971748ab81dd31519da5adce Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 28 Dec 2024 08:47:51 +0300 Subject: [PATCH 286/332] update expected age in test --- menutraversal_test/group_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 8f43ff5..5ffe615 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -430,7 +430,7 @@ }, { "input": "1234", - "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 84\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" }, { "input": "0", From 50c006546cc88138416f88b6fa0b041cf792d24c Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 28 Dec 2024 13:21:03 +0300 Subject: [PATCH 287/332] added code to reset the state and persist it --- devtools/restart_state/main.go | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/devtools/restart_state/main.go b/devtools/restart_state/main.go index 3068a38..5df34fd 100644 --- a/devtools/restart_state/main.go +++ b/devtools/restart_state/main.go @@ -53,25 +53,24 @@ func main() { os.Exit(1) } - // initialize the persister - - // get the state - - // restart the state - - // persist the state - - // exit - st := pe.GetState() if st == nil { logg.ErrorCtxf(ctx, "state fail in devtool", "state", st) - fmt.Errorf("cannot get state") + fmt.Println("cannot get state") os.Exit(1) } - st.Restart() + fmt.Println("The state:", st) - os.Exit(1) + // set empty Code to allow the menu to run from the top + st.Code = []byte{} + + err = pe.Save(sessionId) + if err != nil { + logg.ErrorCtxf(ctx, "failed to persist the state and cache", "error", err) + os.Exit(1) + } + + os.Exit(0) } From 0a332ec5012ba3b03027bbb72af5f470a560b83f Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 30 Dec 2024 08:05:36 +0300 Subject: [PATCH 288/332] chore: ensure swahili language translation. --- internal/handlers/ussd/menuhandler.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 7a188c6..6b6407b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -1367,6 +1367,7 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { var res resource.Result var profileInfo []byte + var defaultValue string var err error flag_firstname_set, _ := h.flagManager.GetFlag("flag_firstname_set") @@ -1383,7 +1384,18 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input if !ok { return res, fmt.Errorf("missing session") } - // Extract the field name from the state machine position. + language, ok := ctx.Value("Language").(lang.Language) + if !ok { + return res, fmt.Errorf("value for 'Language' is not of type lang.Language") + } + code := language.Code + if code == "swa" { + defaultValue = "Haipo" + } else { + defaultValue = "Not Provided" + } + + sm, _ := h.st.Where() parts := strings.SplitN(sm, "_", 2) filename := parts[1] @@ -1400,7 +1412,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FIRST_NAME) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read first name entry with", "key", "error", common.DATA_FIRST_NAME, err) @@ -1412,7 +1424,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_FAMILY_NAME) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read family name entry with", "key", "error", common.DATA_FAMILY_NAME, err) @@ -1425,7 +1437,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_GENDER) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read gender entry with", "key", "error", common.DATA_GENDER, err) @@ -1437,7 +1449,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_YOB) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read year of birth(yob) entry with", "key", "error", common.DATA_YOB, err) @@ -1449,7 +1461,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_LOCATION) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read location entry with", "key", "error", common.DATA_LOCATION, err) @@ -1461,7 +1473,7 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input profileInfo, err = store.ReadEntry(ctx, sessionId, common.DATA_OFFERINGS) if err != nil { if db.IsNotFound(err) { - res.Content = "Not provided" + res.Content = defaultValue break } logg.ErrorCtxf(ctx, "Failed to read offerings entry with", "key", "error", common.DATA_OFFERINGS, err) From db7c9bf56d4f7c173b30947d6c6ec2b511a39acc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 30 Dec 2024 08:07:39 +0300 Subject: [PATCH 289/332] chore: add colon to enhance formatting. --- services/registration/edit_first_name_swa | 2 +- services/registration/edit_location_swa | 2 +- services/registration/edit_yob_swa | 2 +- services/registration/select_gender_swa | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/registration/edit_first_name_swa b/services/registration/edit_first_name_swa index 3fdd986..5776bf0 100644 --- a/services/registration/edit_first_name_swa +++ b/services/registration/edit_first_name_swa @@ -1,2 +1,2 @@ -Jina la kwanza la sasa {{.get_current_profile_info}} +Jina la kwanza la sasa: {{.get_current_profile_info}} Weka majina yako ya kwanza: \ No newline at end of file diff --git a/services/registration/edit_location_swa b/services/registration/edit_location_swa index 0a3476e..179c421 100644 --- a/services/registration/edit_location_swa +++ b/services/registration/edit_location_swa @@ -1,2 +1,2 @@ -Eneo la sasa {{.get_current_profile_info}} +Eneo la sasa: {{.get_current_profile_info}} Weka eneo: \ No newline at end of file diff --git a/services/registration/edit_yob_swa b/services/registration/edit_yob_swa index e0b5708..f923c86 100644 --- a/services/registration/edit_yob_swa +++ b/services/registration/edit_yob_swa @@ -1,2 +1,2 @@ -Mwaka wa sasa wa kuzaliwa {{.get_current_profile_info}} +Mwaka wa sasa wa kuzaliwa: {{.get_current_profile_info}} Weka mwaka wa kuzaliwa \ No newline at end of file diff --git a/services/registration/select_gender_swa b/services/registration/select_gender_swa index b077a0b..39d99d5 100644 --- a/services/registration/select_gender_swa +++ b/services/registration/select_gender_swa @@ -1,2 +1,2 @@ -Jinsia ya sasa {{.get_current_profile_info}} +Jinsia ya sasa: {{.get_current_profile_info}} Chagua jinsia \ No newline at end of file From 0e12c0ee4e7c8371133f39f5295b272609949f26 Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 30 Dec 2024 19:35:45 +0000 Subject: [PATCH 290/332] Add prefix for dumper, format base dump key for pg --- devtools/store/main.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devtools/store/main.go b/devtools/store/main.go index 9262703..908e321 100644 --- a/devtools/store/main.go +++ b/devtools/store/main.go @@ -11,6 +11,7 @@ import ( "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/debug" + "git.defalsify.org/vise.git/db" "git.defalsify.org/vise.git/logging" ) @@ -47,13 +48,14 @@ func main() { store, err := menuStorageService.GetUserdataDb(ctx) if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) + fmt.Fprintf(os.Stderr, "get userdata db: %v\n", err.Error()) os.Exit(1) } + store.SetPrefix(db.DATATYPE_USERDATA) d, err := store.Dump(ctx, []byte(sessionId)) if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) + fmt.Fprintf(os.Stderr, "store dump fail: %v\n", err.Error()) os.Exit(1) } From 3bcd48e5a7849f58032251bd34ff15a0bbb1fd18 Mon Sep 17 00:00:00 2001 From: lash Date: Mon, 30 Dec 2024 19:58:34 +0000 Subject: [PATCH 291/332] Update govise --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e1b7ddb..7b7bff3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 + git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d 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 diff --git a/go.sum b/go.sum index ef7b782..d6dc755 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw= -git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d h1:suOILWHLMQCRc5K3BuzOF8B3wRr3kvJwZFqzGthT/lM= +git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From 1311a0cab9980e32f3085a69da609596217ab0a5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 31 Dec 2024 02:36:28 +0300 Subject: [PATCH 292/332] use the 'send_with_invite' test group in the menu traversal test --- menutraversal_test/menu_traversal_test.go | 5 +++-- menutraversal_test/test_setup.json | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/menutraversal_test/menu_traversal_test.go b/menutraversal_test/menu_traversal_test.go index 28d88db..6b6b3da 100644 --- a/menutraversal_test/menu_traversal_test.go +++ b/menutraversal_test/menu_traversal_test.go @@ -298,9 +298,10 @@ func TestMainMenuSend(t *testing.T) { ctx := context.Background() sessions := testData for _, session := range sessions { - groups := driver.FilterGroupsByName(session.Groups, "send_with_invalid_inputs") + groups := driver.FilterGroupsByName(session.Groups, "send_with_invite") for _, group := range groups { - for _, step := range group.Steps { + for index, step := range group.Steps { + t.Logf("step %v with input %v", index, step.Input) cont, err := en.Exec(ctx, []byte(step.Input)) if err != nil { t.Fatalf("Test case '%s' failed at input '%s': %v", group.Name, step.Input, err) diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index c5860b4..5115de9 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -64,8 +64,8 @@ "expectedContent": "Enter recipient's phone number/address/alias:\n0:Back" }, { - "input": "000", - "expectedContent": "000 is invalid, please try again:\n1:Retry\n9:Quit" + "input": "0@0", + "expectedContent": "0@0 is invalid, please try again:\n1:Retry\n9:Quit" }, { "input": "1", From 3c73fc718896a12f959a6496af287206bcf6deff Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 31 Dec 2024 05:05:39 +0300 Subject: [PATCH 293/332] added a test for the Init func with the different states --- internal/handlers/ussd/menuhandler_test.go | 101 +++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index c01678d..54b1581 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/lang" "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" @@ -15,6 +16,7 @@ import ( "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" + "git.grassecon.net/urdt/ussd/internal/utils" "git.grassecon.net/urdt/ussd/models" "git.grassecon.net/urdt/ussd/common" @@ -119,6 +121,105 @@ func TestNewHandlers(t *testing.T) { }) } +func TestInit(t *testing.T) { + sessionId := "session123" + ctx, store := InitializeTestStore(t) + ctx = context.WithValue(ctx, "SessionId", sessionId) + + fm, err := NewFlagManager(flagsPath) + if err != nil { + t.Fatal(err.Error()) + } + + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + if err != nil { + t.Fatal(err.Error()) + } + + st := state.NewState(128) + ca := cache.NewCache() + + flag_admin_privilege, _ := fm.GetFlag("flag_admin_privilege") + + nonAdminExpectedResult := resource.Result{} + nonAdminExpectedResult.FlagReset = []uint32{flag_admin_privilege} + + tests := []struct { + name string + setup func() (*Handlers, context.Context) + input []byte + expectedResult resource.Result + }{ + { + name: "Handler not ready", + setup: func() (*Handlers, context.Context) { + return &Handlers{}, ctx + }, + input: []byte("1"), + expectedResult: resource.Result{}, + }, + { + name: "State and memory initialization", + setup: func() (*Handlers, context.Context) { + pe := persist.NewPersister(store).WithSession(sessionId).WithContent(st, ca) + h := &Handlers{ + flagManager: fm.parser, + adminstore: adminstore, + pe: pe, + } + return h, context.WithValue(ctx, "SessionId", sessionId) + }, + input: []byte("1"), + expectedResult: resource.Result{ + FlagReset: []uint32{flag_admin_privilege}, + }, + }, + { + name: "Non-admin session initialization", + setup: func() (*Handlers, context.Context) { + pe := persist.NewPersister(store).WithSession("0712345678").WithContent(st, ca) + h := &Handlers{ + flagManager: fm.parser, + adminstore: adminstore, + pe: pe, + } + return h, context.WithValue(context.Background(), "SessionId", "0712345678") + }, + input: []byte("1"), + expectedResult: resource.Result{ + FlagReset: []uint32{flag_admin_privilege}, + }, + }, + { + name: "Move to top node on empty input", + setup: func() (*Handlers, context.Context) { + pe := persist.NewPersister(store).WithSession(sessionId).WithContent(st, ca) + h := &Handlers{ + flagManager: fm.parser, + adminstore: adminstore, + pe: pe, + } + st.Code = []byte("some pending bytecode") + return h, context.WithValue(ctx, "SessionId", sessionId) + }, + input: []byte(""), + expectedResult: resource.Result{ + FlagReset: []uint32{flag_admin_privilege}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h, testCtx := tt.setup() + res, err := h.Init(testCtx, "", tt.input) + + assert.NoError(t, err, "Unexpected error occurred") + assert.Equal(t, res, tt.expectedResult, "Expected result should match actual result") + }) + } +} + func TestCreateAccount(t *testing.T) { sessionId := "session123" ctx, store := InitializeTestStore(t) From be2ea3a2f0fbdf79bfd9d28f2e2b4f09ca37ccb5 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 31 Dec 2024 10:51:29 +0300 Subject: [PATCH 294/332] removed the non-working restart_state devtool --- devtools/restart_state/main.go | 76 ---------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 devtools/restart_state/main.go diff --git a/devtools/restart_state/main.go b/devtools/restart_state/main.go deleted file mode 100644 index 5df34fd..0000000 --- a/devtools/restart_state/main.go +++ /dev/null @@ -1,76 +0,0 @@ -package main - -import ( - "context" - "flag" - "fmt" - "os" - "path" - - "git.defalsify.org/vise.git/logging" - "git.grassecon.net/urdt/ussd/config" - "git.grassecon.net/urdt/ussd/initializers" - "git.grassecon.net/urdt/ussd/internal/storage" -) - -var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") -) - -func init() { - initializers.LoadEnvVariables() -} - -func main() { - config.LoadConfig() - - var dbDir string - var sessionId string - var database string - - flag.StringVar(&sessionId, "session-id", "075xx2123", "session id") - flag.StringVar(&database, "db", "gdbm", "database to be used") - flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from") - flag.Parse() - - ctx := context.Background() - ctx = context.WithValue(ctx, "SessionId", sessionId) - ctx = context.WithValue(ctx, "Database", database) - - resourceDir := scriptDir - menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir) - - err := menuStorageService.EnsureDbDir() - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - - pe, err := menuStorageService.GetPersister(ctx) - if err != nil { - fmt.Fprintf(os.Stderr, err.Error()) - os.Exit(1) - } - - st := pe.GetState() - - if st == nil { - logg.ErrorCtxf(ctx, "state fail in devtool", "state", st) - fmt.Println("cannot get state") - os.Exit(1) - } - - fmt.Println("The state:", st) - - // set empty Code to allow the menu to run from the top - st.Code = []byte{} - - err = pe.Save(sessionId) - if err != nil { - logg.ErrorCtxf(ctx, "failed to persist the state and cache", "error", err) - os.Exit(1) - } - - os.Exit(0) -} From 4ea52bf3fbb0f605d1296c710fd6a823fa8c4bad Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 31 Dec 2024 11:16:43 +0300 Subject: [PATCH 295/332] removed unused code --- internal/handlers/ussd/menuhandler_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 54b1581..34c8e76 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -141,9 +141,6 @@ func TestInit(t *testing.T) { flag_admin_privilege, _ := fm.GetFlag("flag_admin_privilege") - nonAdminExpectedResult := resource.Result{} - nonAdminExpectedResult.FlagReset = []uint32{flag_admin_privilege} - tests := []struct { name string setup func() (*Handlers, context.Context) From c7dbe1d88f4174ec1b54d71f4b23980105a96668 Mon Sep 17 00:00:00 2001 From: lash Date: Tue, 31 Dec 2024 08:30:08 +0000 Subject: [PATCH 296/332] Remove obsolete subprefix strings --- debug/db_debug.go | 4 ---- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/debug/db_debug.go b/debug/db_debug.go index ed2dd66..05a238b 100644 --- a/debug/db_debug.go +++ b/debug/db_debug.go @@ -11,13 +11,9 @@ import ( func init() { DebugCap |= 1 dbTypStr[db.DATATYPE_STATE] = "internal state" - dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account" - dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account created" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key" - dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin" - dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name" dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth" diff --git a/go.mod b/go.mod index 7b7bff3..a423398 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d + git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b 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 diff --git a/go.sum b/go.sum index d6dc755..95b4843 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d h1:suOILWHLMQCRc5K3BuzOF8B3wRr3kvJwZFqzGthT/lM= -git.defalsify.org/vise.git v0.2.3-0.20241230195348-d746f4696a2d/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b h1:s3tDS07BRCyCIxH8e9eTAsf5uqYIVGmvAmwpsT5IYT0= +git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From 7a535f796a79f39955f702ce2bfb2093f8f03d0e Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 31 Dec 2024 11:41:04 +0300 Subject: [PATCH 297/332] output the value as a string --- devtools/store/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/store/main.go b/devtools/store/main.go index 908e321..8bd4d16 100644 --- a/devtools/store/main.go +++ b/devtools/store/main.go @@ -69,7 +69,7 @@ func main() { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } - fmt.Printf("%vValue: %v\n\n", o, v) + fmt.Printf("%vValue: %v\n\n", o, string(v)) } err = store.Close() From cd58f5ae33b4679d9c9a2a133ffdce36b72ead76 Mon Sep 17 00:00:00 2001 From: lash Date: Tue, 31 Dec 2024 08:55:25 +0000 Subject: [PATCH 298/332] Upgrade govise --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a423398..16ccdc3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b + git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9 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 diff --git a/go.sum b/go.sum index 95b4843..9086cd8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b h1:s3tDS07BRCyCIxH8e9eTAsf5uqYIVGmvAmwpsT5IYT0= -git.defalsify.org/vise.git v0.2.3-0.20241231082805-35b41792f61b/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9 h1:O3m+NgWDWtJm8OculT99c4bDMAO4xLe2c8hpCKpsd9g= +git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= 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= From ffd5be1f1f37b3679ff4a453ffde5977489f7212 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 2 Jan 2025 12:12:52 +0300 Subject: [PATCH 299/332] add quit option on view profile --- services/registration/view_profile.vis | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/registration/view_profile.vis b/services/registration/view_profile.vis index a7ffee4..4f4947c 100644 --- a/services/registration/view_profile.vis +++ b/services/registration/view_profile.vis @@ -4,5 +4,8 @@ LOAD reset_incorrect 6 CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 MOUT back 0 +MOUT quit 9 HALT INCMP _ 0 +INCMP quit 9 +INCMP . * From e980586910861e4b316c7f8d1f1c09464403a7a1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 2 Jan 2025 12:15:57 +0300 Subject: [PATCH 300/332] chore: repeat same node on invalid menu choice --- services/registration/balances.vis | 1 + services/registration/community_balance.vis | 1 + services/registration/edit_profile.vis | 1 + services/registration/my_account.vis | 1 + services/registration/my_balance.vis | 1 + 5 files changed, 5 insertions(+) diff --git a/services/registration/balances.vis b/services/registration/balances.vis index aef397f..9a346d5 100644 --- a/services/registration/balances.vis +++ b/services/registration/balances.vis @@ -7,3 +7,4 @@ HALT INCMP _ 0 INCMP my_balance 1 INCMP community_balance 2 +INCMP . * diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index f3e0ae1..fad90cc 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -9,3 +9,4 @@ MOUT quit 9 HALT INCMP _ 0 INCMP quit 9 +INCMP . * diff --git a/services/registration/edit_profile.vis b/services/registration/edit_profile.vis index e5ee12b..0f0adb7 100644 --- a/services/registration/edit_profile.vis +++ b/services/registration/edit_profile.vis @@ -20,3 +20,4 @@ INCMP edit_yob 4 INCMP edit_location 5 INCMP edit_offerings 6 INCMP view_profile 7 +INCMP . * diff --git a/services/registration/my_account.vis b/services/registration/my_account.vis index 2b6289e..e3956d2 100644 --- a/services/registration/my_account.vis +++ b/services/registration/my_account.vis @@ -14,3 +14,4 @@ INCMP balances 3 INCMP check_statement 4 INCMP pin_management 5 INCMP address 6 +INCMP . * diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index 9144da9..b6094c0 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -9,3 +9,4 @@ MOUT quit 9 HALT INCMP _ 0 INCMP quit 9 +INCMP . * From e666c586444bd89cb298cee8bbb589e83ce0aa37 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 2 Jan 2025 12:17:28 +0300 Subject: [PATCH 301/332] start primary selectors with 1 --- services/registration/change_language.vis | 8 ++++---- services/registration/select_language.vis | 8 ++++---- services/registration/terms.vis | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/services/registration/change_language.vis b/services/registration/change_language.vis index 05ca95b..8b1def9 100644 --- a/services/registration/change_language.vis +++ b/services/registration/change_language.vis @@ -2,9 +2,9 @@ LOAD reset_account_authorized 0 LOAD reset_incorrect 0 CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 -MOUT english 0 -MOUT kiswahili 1 +MOUT english 1 +MOUT kiswahili 2 HALT -INCMP set_default 0 -INCMP set_swa 1 +INCMP set_default 1 +INCMP set_swa 2 INCMP . * diff --git a/services/registration/select_language.vis b/services/registration/select_language.vis index 54f08e9..0f7f298 100644 --- a/services/registration/select_language.vis +++ b/services/registration/select_language.vis @@ -1,6 +1,6 @@ -MOUT english 0 -MOUT kiswahili 1 +MOUT english 1 +MOUT kiswahili 2 HALT -INCMP set_eng 0 -INCMP set_swa 1 +INCMP set_eng 1 +INCMP set_swa 2 INCMP . * diff --git a/services/registration/terms.vis b/services/registration/terms.vis index f04bdf4..372b6ca 100644 --- a/services/registration/terms.vis +++ b/services/registration/terms.vis @@ -1,5 +1,5 @@ -MOUT yes 0 -MOUT no 1 +MOUT yes 1 +MOUT no 2 HALT -INCMP create_pin 0 +INCMP create_pin 1 INCMP quit * From 48d63fb43ff57ea7dc16c24f1ac3b7a715d49958 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:16:38 +0300 Subject: [PATCH 302/332] added pin.go to contain all PIN related functionality --- common/pin.go | 14 ++++++++++++++ internal/handlers/ussd/menuhandler.go | 16 ++-------------- internal/handlers/ussd/menuhandler_test.go | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 common/pin.go diff --git a/common/pin.go b/common/pin.go new file mode 100644 index 0000000..4d46f12 --- /dev/null +++ b/common/pin.go @@ -0,0 +1,14 @@ +package common + +import "regexp" + +// Define the regex pattern as a constant +const ( + pinPattern = `^\d{4}$` +) + +// checks whether the given input is a 4 digit number +func IsValidPIN(pin string) bool { + match, _ := regexp.MatchString(pinPattern, pin) + return match +} diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 640517f..c20d75b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "path" - "regexp" "strconv" "strings" @@ -34,17 +33,6 @@ var ( translationDir = path.Join(scriptDir, "locale") ) -// Define the regex patterns as constants -const ( - pinPattern = `^\d{4}$` -) - -// checks whether the given input is a 4 digit number -func isValidPIN(pin string) bool { - match, _ := regexp.MatchString(pinPattern, pin) - return match -} - // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser @@ -281,7 +269,7 @@ func (h *Handlers) VerifyNewPin(ctx context.Context, sym string, input []byte) ( flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") pinInput := string(input) // Validate that the PIN is a 4-digit number. - if isValidPIN(pinInput) { + if common.IsValidPIN(pinInput) { res.FlagSet = append(res.FlagSet, flag_valid_pin) } else { res.FlagReset = append(res.FlagReset, flag_valid_pin) @@ -306,7 +294,7 @@ func (h *Handlers) SaveTemporaryPin(ctx context.Context, sym string, input []byt accountPIN := string(input) // Validate that the PIN is a 4-digit number. - if !isValidPIN(accountPIN) { + if !common.IsValidPIN(accountPIN) { res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 34c8e76..0d66f72 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1544,9 +1544,9 @@ func TestIsValidPIN(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - actual := isValidPIN(tt.pin) + actual := common.IsValidPIN(tt.pin) if actual != tt.expected { - t.Errorf("isValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected) + t.Errorf("IsValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected) } }) } From 5ca6a74274931fd49f8081a9fa3e68ee524a1eb3 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:18:49 +0300 Subject: [PATCH 303/332] move PIN test to the common package --- common/pin_test.go | 56 ++++++++++++++++++++++ internal/handlers/ussd/menuhandler_test.go | 53 -------------------- 2 files changed, 56 insertions(+), 53 deletions(-) create mode 100644 common/pin_test.go diff --git a/common/pin_test.go b/common/pin_test.go new file mode 100644 index 0000000..9012024 --- /dev/null +++ b/common/pin_test.go @@ -0,0 +1,56 @@ +package common + +import "testing" + +func TestIsValidPIN(t *testing.T) { + tests := []struct { + name string + pin string + expected bool + }{ + { + name: "Valid PIN with 4 digits", + pin: "1234", + expected: true, + }, + { + name: "Valid PIN with leading zeros", + pin: "0001", + expected: true, + }, + { + name: "Invalid PIN with less than 4 digits", + pin: "123", + expected: false, + }, + { + name: "Invalid PIN with more than 4 digits", + pin: "12345", + expected: false, + }, + { + name: "Invalid PIN with letters", + pin: "abcd", + expected: false, + }, + { + name: "Invalid PIN with special characters", + pin: "12@#", + expected: false, + }, + { + name: "Empty PIN", + pin: "", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := IsValidPIN(tt.pin) + if actual != tt.expected { + t.Errorf("IsValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected) + } + }) + } +} diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 0d66f72..9b3dc7b 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1499,59 +1499,6 @@ func TestQuit(t *testing.T) { } } -func TestIsValidPIN(t *testing.T) { - tests := []struct { - name string - pin string - expected bool - }{ - { - name: "Valid PIN with 4 digits", - pin: "1234", - expected: true, - }, - { - name: "Valid PIN with leading zeros", - pin: "0001", - expected: true, - }, - { - name: "Invalid PIN with less than 4 digits", - pin: "123", - expected: false, - }, - { - name: "Invalid PIN with more than 4 digits", - pin: "12345", - expected: false, - }, - { - name: "Invalid PIN with letters", - pin: "abcd", - expected: false, - }, - { - name: "Invalid PIN with special characters", - pin: "12@#", - expected: false, - }, - { - name: "Empty PIN", - pin: "", - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - actual := common.IsValidPIN(tt.pin) - if actual != tt.expected { - t.Errorf("IsValidPIN(%q) = %v; expected %v", tt.pin, actual, tt.expected) - } - }) - } -} - func TestValidateAmount(t *testing.T) { fm, err := NewFlagManager(flagsPath) if err != nil { From c899c098f62994536f6952626c3feb7e5d041ab7 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:20:01 +0300 Subject: [PATCH 304/332] updated the expected age --- internal/handlers/ussd/menuhandler_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 9b3dc7b..43f80a2 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1745,7 +1745,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, @@ -1757,7 +1757,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, @@ -1769,7 +1769,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, From fd1ac85a1b2cbcdb233df8dc9b34cbfdf3e4de37 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:43:38 +0300 Subject: [PATCH 305/332] add code to Hash and Verify the PIN --- common/pin.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/common/pin.go b/common/pin.go index 4d46f12..6db9d15 100644 --- a/common/pin.go +++ b/common/pin.go @@ -1,6 +1,10 @@ package common -import "regexp" +import ( + "regexp" + + "golang.org/x/crypto/bcrypt" +) // Define the regex pattern as a constant const ( @@ -12,3 +16,18 @@ func IsValidPIN(pin string) bool { match, _ := regexp.MatchString(pinPattern, pin) return match } + +// HashPIN uses bcrypt with 8 salt rounds to hash the PIN +func HashPIN(pin string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(pin), 8) + if err != nil { + return "", err + } + return string(hash), nil +} + +// VerifyPIN compareS the hashed PIN with the plaintext PIN +func VerifyPIN(hashedPIN, pin string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hashedPIN), []byte(pin)) + return err == nil +} From d95c7abea46dd84049262571ab9481922e089c33 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:45:18 +0300 Subject: [PATCH 306/332] return if the PIN is not a match, and hash the PIN before saving it --- internal/handlers/ussd/menuhandler.go | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index c20d75b..645e74c 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -356,11 +356,19 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt res.FlagReset = append(res.FlagReset, flag_pin_mismatch) } else { res.FlagSet = append(res.FlagSet, flag_pin_mismatch) + return res, nil } - // If matched, save the confirmed PIN as the new account PIN - err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + + // Hash the PIN + hashedPIN, err := common.HashPIN(string(temporaryPin)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) + logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + } + + // save the hashed PIN as the new account PIN + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", common.DATA_ACCOUNT_PIN, "hashedPIN value", hashedPIN, "error", err) return res, err } return res, nil @@ -392,11 +400,18 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte res.FlagSet = append(res.FlagSet, flag_pin_set) } else { res.FlagSet = []uint32{flag_pin_mismatch} + return res, nil } - err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + // Hash the PIN + hashedPIN, err := common.HashPIN(string(temporaryPin)) if err != nil { - logg.ErrorCtxf(ctx, "failed to write temporaryPin entry with", "key", common.DATA_ACCOUNT_PIN, "value", temporaryPin, "error", err) + logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + } + + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to write DATA_ACCOUNT_PIN entry with", "key", common.DATA_ACCOUNT_PIN, "value", hashedPIN, "error", err) return res, err } From 99a4d3ff421f7da6c22e3bba7d155b6f0b82e982 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 13:49:57 +0300 Subject: [PATCH 307/332] verify the PIN input against the hashed PIN --- internal/handlers/ussd/menuhandler.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 645e74c..0829011 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -725,7 +725,7 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, err } if len(input) == 4 { - if bytes.Equal(input, AccountPin) { + if common.VerifyPIN(string(AccountPin), string(input)) { if h.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) @@ -1403,7 +1403,6 @@ func (h *Handlers) GetCurrentProfileInfo(ctx context.Context, sym string, input defaultValue = "Not Provided" } - sm, _ := h.st.Where() parts := strings.SplitN(sm, "_", 2) filename := parts[1] From 98db85511be78d3238f80fb74d77817ef8b799d2 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 14:37:45 +0300 Subject: [PATCH 308/332] hash the PIN in the ResetOthersPin function --- internal/handlers/ussd/menuhandler.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 0829011..3919595 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -363,6 +363,7 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt hashedPIN, err := common.HashPIN(string(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + return res, err } // save the hashed PIN as the new account PIN @@ -407,6 +408,7 @@ func (h *Handlers) VerifyCreatePin(ctx context.Context, sym string, input []byte hashedPIN, err := common.HashPIN(string(temporaryPin)) if err != nil { logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + return res, err } err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN)) @@ -952,7 +954,15 @@ func (h *Handlers) ResetOthersPin(ctx context.Context, sym string, input []byte) logg.ErrorCtxf(ctx, "failed to read temporaryPin entry with", "key", common.DATA_TEMPORARY_VALUE, "error", err) return res, err } - err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(temporaryPin)) + + // Hash the PIN + hashedPIN, err := common.HashPIN(string(temporaryPin)) + if err != nil { + logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + return res, err + } + + err = store.WriteEntry(ctx, string(blockedPhonenumber), common.DATA_ACCOUNT_PIN, []byte(hashedPIN)) if err != nil { return res, nil } From 82b4365d161ab9e16e0506f61439f4c124a6be96 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 14:38:22 +0300 Subject: [PATCH 309/332] hash the PIN in TestAuthorize --- internal/handlers/ussd/menuhandler_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 43f80a2..12ed5c2 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1047,7 +1047,14 @@ func TestAuthorize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(accountPIN)) + // Hash the PIN + hashedPIN, err := common.HashPIN(accountPIN) + if err != nil { + logg.ErrorCtxf(ctx, "failed to hash temporaryPin", "error", err) + t.Fatal(err) + } + + err = store.WriteEntry(ctx, sessionId, common.DATA_ACCOUNT_PIN, []byte(hashedPIN)) if err != nil { t.Fatal(err) } From ca8df5989a4eac8a723c8d2b1bf1c580d326c3dc Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 15:15:52 +0300 Subject: [PATCH 310/332] updated expected age in test --- menutraversal_test/group_test.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 5ffe615..ac12de3 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -430,7 +430,7 @@ }, { "input": "1234", - "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" }, { "input": "0", From 29ce4b83bdff170d3e8bb6d24e9754da9dcb3dc4 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 15:22:07 +0300 Subject: [PATCH 311/332] added tests for HashPIN and VerifyPIN --- common/pin_test.go | 98 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/common/pin_test.go b/common/pin_test.go index 9012024..1b11d2c 100644 --- a/common/pin_test.go +++ b/common/pin_test.go @@ -1,6 +1,10 @@ package common -import "testing" +import ( + "testing" + + "golang.org/x/crypto/bcrypt" +) func TestIsValidPIN(t *testing.T) { tests := []struct { @@ -54,3 +58,95 @@ func TestIsValidPIN(t *testing.T) { }) } } + +func TestHashPIN(t *testing.T) { + tests := []struct { + name string + pin string + }{ + { + name: "Valid PIN with 4 digits", + pin: "1234", + }, + { + name: "Valid PIN with leading zeros", + pin: "0001", + }, + { + name: "Empty PIN", + pin: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hashedPIN, err := HashPIN(tt.pin) + if err != nil { + t.Errorf("HashPIN(%q) returned an error: %v", tt.pin, err) + return + } + + if hashedPIN == "" { + t.Errorf("HashPIN(%q) returned an empty hash", tt.pin) + } + + // Ensure the hash can be verified with bcrypt + err = bcrypt.CompareHashAndPassword([]byte(hashedPIN), []byte(tt.pin)) + if tt.pin != "" && err != nil { + t.Errorf("HashPIN(%q) produced a hash that does not match: %v", tt.pin, err) + } + }) + } +} + +func TestVerifyPIN(t *testing.T) { + tests := []struct { + name string + pin string + hashedPIN string + shouldPass bool + }{ + { + name: "Valid PIN verification", + pin: "1234", + hashedPIN: hashPINHelper("1234"), + shouldPass: true, + }, + { + name: "Invalid PIN verification with incorrect PIN", + pin: "5678", + hashedPIN: hashPINHelper("1234"), + shouldPass: false, + }, + { + name: "Invalid PIN verification with empty PIN", + pin: "", + hashedPIN: hashPINHelper("1234"), + shouldPass: false, + }, + { + name: "Invalid PIN verification with invalid hash", + pin: "1234", + hashedPIN: "invalidhash", + shouldPass: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := VerifyPIN(tt.hashedPIN, tt.pin) + if result != tt.shouldPass { + t.Errorf("VerifyPIN(%q, %q) = %v; expected %v", tt.hashedPIN, tt.pin, result, tt.shouldPass) + } + }) + } +} + +// Helper function to hash a PIN for testing purposes +func hashPINHelper(pin string) string { + hashedPIN, err := HashPIN(pin) + if err != nil { + panic("Failed to hash PIN for test setup: " + err.Error()) + } + return hashedPIN +} From 491b7424a9176e4d646dca82234e4e3b49968879 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Thu, 2 Jan 2025 16:01:19 +0300 Subject: [PATCH 312/332] point to the correct ./devtools/admin_numbers directory --- internal/handlers/handlerservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index 1da28c3..b2a6f2f 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -43,7 +43,7 @@ func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResour if err != nil { return nil, err } - adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") + adminstore, err := utils.NewAdminStore(ctx, "./devtools/admin_numbers") if err != nil { return nil, err } From f1fd690a7ba989f252a826d2e8463752018f4d14 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 2 Jan 2025 17:37:26 +0300 Subject: [PATCH 313/332] update expected content --- menutraversal_test/group_test.json | 6 +++--- menutraversal_test/test_setup.json | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/menutraversal_test/group_test.json b/menutraversal_test/group_test.json index 5ffe615..f35beb9 100644 --- a/menutraversal_test/group_test.json +++ b/menutraversal_test/group_test.json @@ -62,10 +62,10 @@ }, { "input": "1234", - "expectedContent": "Select language:\n0:English\n1:Kiswahili" + "expectedContent": "Select language:\n1:English\n2:Kiswahili" }, { - "input": "0", + "input": "1", "expectedContent": "Your language change request was successful.\n0:Back\n9:Quit" }, { @@ -430,7 +430,7 @@ }, { "input": "1234", - "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 79\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back" + "expectedContent": "My profile:\nName: foo bar\nGender: male\nAge: 80\nLocation: Kilifi\nYou provide: Bananas\n\n0:Back\n9:Quit" }, { "input": "0", diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json index 5115de9..8728640 100644 --- a/menutraversal_test/test_setup.json +++ b/menutraversal_test/test_setup.json @@ -7,14 +7,14 @@ "steps": [ { "input": "", - "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili" + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n1:English\n2:Kiswahili" }, { - "input": "0", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No" + "input": "1", + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n1:Yes\n2:No" }, { - "input": "0", + "input": "1", "expectedContent": "Please enter a new four number PIN for your account:\n0:Exit" }, { @@ -40,14 +40,14 @@ "steps": [ { "input": "", - "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n0:English\n1:Kiswahili" - }, - { - "input": "0", - "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No" + "expectedContent": "Welcome to Sarafu Network\nPlease select a language\n1:English\n2:Kiswahili" }, { "input": "1", + "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n1:Yes\n2:No" + }, + { + "input": "2", "expectedContent": "Thank you for using Sarafu. Goodbye!" } ] From 3830c12a57ecbb8c92bd6387b1acdd623b81fbbc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Thu, 2 Jan 2025 17:42:03 +0300 Subject: [PATCH 314/332] update tests --- internal/handlers/ussd/menuhandler_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 34c8e76..2b168f2 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -1798,7 +1798,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, @@ -1810,7 +1810,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Jina: %s\nJinsia: %s\nUmri: %s\nEneo: %s\nUnauza: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, @@ -1822,7 +1822,7 @@ func TestGetProfile(t *testing.T) { result: resource.Result{ Content: fmt.Sprintf( "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", - "John Doee", "Male", "48", "Kilifi", "Bananas", + "John Doee", "Male", "49", "Kilifi", "Bananas", ), }, }, From 91dc9ce82f9cb5ca3e233013f8b0cccf3f0b4e5a Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Fri, 3 Jan 2025 11:10:07 +0300 Subject: [PATCH 315/332] tests: add sample pin/hash pair from migration dataset --- common/pin_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/common/pin_test.go b/common/pin_test.go index 1b11d2c..f10ad6b 100644 --- a/common/pin_test.go +++ b/common/pin_test.go @@ -99,6 +99,27 @@ func TestHashPIN(t *testing.T) { } } +func TestVerifyMigratedHashPin(t *testing.T) { + tests := []struct { + pin string + hash string + }{ + { + pin: "1234", + hash: "$2b$08$dTvIGxCCysJtdvrSnaLStuylPoOS/ZLYYkxvTeR5QmTFY3TSvPQC6", + }, + } + + for _, tt := range tests { + t.Run(tt.pin, func(t *testing.T) { + ok := VerifyPIN(tt.hash, tt.pin) + if !ok { + t.Errorf("VerifyPIN could not verify migrated PIN: %v", tt.pin, ok) + } + }) + } +} + func TestVerifyPIN(t *testing.T) { tests := []struct { name string From c26f5683f6a752b0f23fdbc9980a665d31f4555a Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 3 Jan 2025 11:17:09 +0300 Subject: [PATCH 316/332] removed second unused argument --- common/pin_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/pin_test.go b/common/pin_test.go index f10ad6b..154ab06 100644 --- a/common/pin_test.go +++ b/common/pin_test.go @@ -114,7 +114,7 @@ func TestVerifyMigratedHashPin(t *testing.T) { t.Run(tt.pin, func(t *testing.T) { ok := VerifyPIN(tt.hash, tt.pin) if !ok { - t.Errorf("VerifyPIN could not verify migrated PIN: %v", tt.pin, ok) + t.Errorf("VerifyPIN could not verify migrated PIN: %v", tt.pin) } }) } From 9d6e25e184e1647e97b9f9bfece69f39495f1c15 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Fri, 3 Jan 2025 11:24:24 +0300 Subject: [PATCH 317/332] revert to previous state for the adminstore --- internal/handlers/handlerservice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/handlers/handlerservice.go b/internal/handlers/handlerservice.go index b2a6f2f..1da28c3 100644 --- a/internal/handlers/handlerservice.go +++ b/internal/handlers/handlerservice.go @@ -43,7 +43,7 @@ func NewLocalHandlerService(ctx context.Context, fp string, debug bool, dbResour if err != nil { return nil, err } - adminstore, err := utils.NewAdminStore(ctx, "./devtools/admin_numbers") + adminstore, err := utils.NewAdminStore(ctx, "admin_numbers") if err != nil { return nil, err } From 162e6c19343a4b12f2eb5230686da1684e7fb8ee Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Fri, 3 Jan 2025 11:26:56 +0300 Subject: [PATCH 318/332] fix: language change --- internal/utils/isocode.go | 6 +++--- services/registration/change_language.vis | 2 +- services/registration/set_default.vis | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 services/registration/set_default.vis diff --git a/internal/utils/isocode.go b/internal/utils/isocode.go index 3bdfbeb..692b7bb 100644 --- a/internal/utils/isocode.go +++ b/internal/utils/isocode.go @@ -1,9 +1,9 @@ package utils var isoCodes = map[string]bool{ - "eng": true, // English - "swa": true, // Swahili - + "eng": true, // English + "swa": true, // Swahili + "default": true, // Default language: English } func IsValidISO639(code string) bool { diff --git a/services/registration/change_language.vis b/services/registration/change_language.vis index 8b1def9..f20bcfb 100644 --- a/services/registration/change_language.vis +++ b/services/registration/change_language.vis @@ -5,6 +5,6 @@ CATCH pin_entry flag_account_authorized 0 MOUT english 1 MOUT kiswahili 2 HALT -INCMP set_default 1 +INCMP set_eng 1 INCMP set_swa 2 INCMP . * diff --git a/services/registration/set_default.vis b/services/registration/set_default.vis new file mode 100644 index 0000000..b66a1b7 --- /dev/null +++ b/services/registration/set_default.vis @@ -0,0 +1,4 @@ +LOAD set_language 6 +RELOAD set_language +CATCH terms flag_account_created 0 +MOVE language_changed From f1b258fa6ddecdc455776d9a4dcd2dac80fd5200 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 07:29:22 +0000 Subject: [PATCH 319/332] Factor out at code --- cmd/africastalking/main.go | 2 +- .../{at_session_handler.go => at/server.go} | 22 +- .../http/{http_test.go => at/server_test.go} | 215 ---------------- internal/http/server.go | 12 +- internal/http/server_test.go | 229 ++++++++++++++++++ 5 files changed, 250 insertions(+), 230 deletions(-) rename internal/http/{at_session_handler.go => at/server.go} (80%) rename internal/http/{http_test.go => at/server_test.go} (55%) create mode 100644 internal/http/server_test.go diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index ca88978..1f142da 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -23,7 +23,7 @@ import ( "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" - httpserver "git.grassecon.net/urdt/ussd/internal/http" + httpserver "git.grassecon.net/urdt/ussd/internal/http/at" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/remote" ) diff --git a/internal/http/at_session_handler.go b/internal/http/at/server.go similarity index 80% rename from internal/http/at_session_handler.go rename to internal/http/at/server.go index 25da954..9cade3d 100644 --- a/internal/http/at_session_handler.go +++ b/internal/http/at/server.go @@ -4,16 +4,22 @@ import ( "io" "net/http" + "git.defalsify.org/vise.git/logging" "git.grassecon.net/urdt/ussd/internal/handlers" + httpserver "git.grassecon.net/urdt/ussd/internal/http" +) + +var ( + logg = logging.NewVanilla().WithDomain("atserver") ) type ATSessionHandler struct { - *SessionHandler + *httpserver.SessionHandler } func NewATSessionHandler(h handlers.RequestHandler) *ATSessionHandler { return &ATSessionHandler{ - SessionHandler: ToSessionHandler(h), + SessionHandler: httpserver.ToSessionHandler(h), } } @@ -31,14 +37,14 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) cfg.SessionId, err = rp.GetSessionId(req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) - ash.writeError(w, 400, err) + ash.WriteError(w, 400, err) return } rqs.Config = cfg rqs.Input, err = rp.GetInput(req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) - ash.writeError(w, 400, err) + ash.WriteError(w, 400, err) return } @@ -53,7 +59,7 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) } if code != 200 { - ash.writeError(w, 500, err) + ash.WriteError(w, 500, err) return } @@ -61,13 +67,13 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) w.Header().Set("Content-Type", "text/plain") rqs, err = ash.Output(rqs) if err != nil { - ash.writeError(w, 500, err) + ash.WriteError(w, 500, err) return } rqs, err = ash.Reset(rqs) if err != nil { - ash.writeError(w, 500, err) + ash.WriteError(w, 500, err) return } } @@ -89,4 +95,4 @@ func (ash *ATSessionHandler) Output(rqs handlers.RequestSession) (handlers.Reque _, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer) return rqs, err -} \ No newline at end of file +} diff --git a/internal/http/http_test.go b/internal/http/at/server_test.go similarity index 55% rename from internal/http/http_test.go rename to internal/http/at/server_test.go index 14bb90a..d49f9ce 100644 --- a/internal/http/http_test.go +++ b/internal/http/at/server_test.go @@ -1,7 +1,6 @@ package http import ( - "bytes" "context" "errors" "io" @@ -16,16 +15,6 @@ import ( "git.grassecon.net/urdt/ussd/internal/testutil/mocks/httpmocks" ) -// 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 TestNewATSessionHandler(t *testing.T) { mockHandler := &httpmocks.MockRequestHandler{} ash := NewATSessionHandler(mockHandler) @@ -242,208 +231,4 @@ func TestATSessionHandler_Output(t *testing.T) { } } -func TestSessionHandler_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: handlers.ErrSessionMissing, - expectedStatus: http.StatusBadRequest, - }, - { - name: "Process Error", - sessionID: "123", - input: []byte("test input"), - processErr: handlers.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 handlers.RequestSession) (handlers.RequestSession, error) { - return rs, tt.processErr - }, - OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { - return rs, tt.outputErr - }, - ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { - return rs, tt.resetErr - }, - GetRequestParserFunc: func() handlers.RequestParser { - return mockRequestParser - }, - GetConfigFunc: func() engine.Config { - return engine.Config{} - }, - } - - sessionHandler := ToSessionHandler(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 TestSessionHandler_writeError(t *testing.T) { - handler := &SessionHandler{} - 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: handlers.ErrSessionMissing, - }, - { - name: "Invalid Request Type", - request: invalidRequestType{}, - expectedID: "", - expectedError: handlers.ErrInvalidRequest, - }, - } - - parser := &DefaultRequestParser{} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - id, err := parser.GetSessionId(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: handlers.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) - } - }) - } -} diff --git a/internal/http/server.go b/internal/http/server.go index a6239c4..df15407 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -52,7 +52,7 @@ func ToSessionHandler(h handlers.RequestHandler) *SessionHandler { } } -func (f *SessionHandler) writeError(w http.ResponseWriter, code int, err error) { +func (f *SessionHandler) WriteError(w http.ResponseWriter, code int, err error) { s := err.Error() w.Header().Set("Content-Length", strconv.Itoa(len(s))) w.WriteHeader(code) @@ -78,13 +78,13 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { cfg.SessionId, err = rp.GetSessionId(req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) - f.writeError(w, 400, err) + f.WriteError(w, 400, err) } rqs.Config = cfg rqs.Input, err = rp.GetInput(req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) - f.writeError(w, 400, err) + f.WriteError(w, 400, err) return } @@ -101,7 +101,7 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { } if code != 200 { - f.writeError(w, 500, err) + f.WriteError(w, 500, err) return } @@ -110,11 +110,11 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { rqs, err = f.Output(rqs) rqs, perr = f.Reset(rqs) if err != nil { - f.writeError(w, 500, err) + f.WriteError(w, 500, err) return } if perr != nil { - f.writeError(w, 500, perr) + f.WriteError(w, 500, perr) return } } diff --git a/internal/http/server_test.go b/internal/http/server_test.go new file mode 100644 index 0000000..a46f98e --- /dev/null +++ b/internal/http/server_test.go @@ -0,0 +1,229 @@ +package http + +import ( + "bytes" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "git.defalsify.org/vise.git/engine" + "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/testutil/mocks/httpmocks" +) + +// 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 TestSessionHandler_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: handlers.ErrSessionMissing, + expectedStatus: http.StatusBadRequest, + }, + { + name: "Process Error", + sessionID: "123", + input: []byte("test input"), + processErr: handlers.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 handlers.RequestSession) (handlers.RequestSession, error) { + return rs, tt.processErr + }, + OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { + return rs, tt.outputErr + }, + ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) { + return rs, tt.resetErr + }, + GetRequestParserFunc: func() handlers.RequestParser { + return mockRequestParser + }, + GetConfigFunc: func() engine.Config { + return engine.Config{} + }, + } + + sessionHandler := ToSessionHandler(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 TestSessionHandler_WriteError(t *testing.T) { + handler := &SessionHandler{} + 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: handlers.ErrSessionMissing, + }, + { + name: "Invalid Request Type", + request: invalidRequestType{}, + expectedID: "", + expectedError: handlers.ErrInvalidRequest, + }, + } + + parser := &DefaultRequestParser{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + id, err := parser.GetSessionId(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: handlers.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) + } + }) + } +} From 67007fcd488994819339422cbc701b7b578b0ac9 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 07:35:28 +0000 Subject: [PATCH 320/332] Factor out gdbm package --- internal/storage/{ => gdbm}/gdbm.go | 5 +++++ internal/storage/storageservice.go | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) rename internal/storage/{ => gdbm}/gdbm.go (95%) diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm/gdbm.go similarity index 95% rename from internal/storage/gdbm.go rename to internal/storage/gdbm/gdbm.go index 31ebf47..dab767a 100644 --- a/internal/storage/gdbm.go +++ b/internal/storage/gdbm/gdbm.go @@ -6,6 +6,11 @@ import ( "git.defalsify.org/vise.git/db" gdbmdb "git.defalsify.org/vise.git/db/gdbm" "git.defalsify.org/vise.git/lang" + "git.defalsify.org/vise.git/logging" +) + +var ( + logg = logging.NewVanilla().WithDomain("gdbmstorage") ) var ( diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index ca28bbb..7856095 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -13,6 +13,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/initializers" + gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/gdbm" ) var ( @@ -75,7 +76,7 @@ func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.D connStr := buildConnStr() err = newDb.Connect(ctx, connStr) } else { - newDb = NewThreadGdbmDb() + newDb = gdbmstorage.NewThreadGdbmDb() storeFile := path.Join(ms.dbDir, fileName) err = newDb.Connect(ctx, storeFile) } From f65c458daa6315b18ee8ac7dec7a8a96a4881bae Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 4 Jan 2025 10:35:59 +0300 Subject: [PATCH 321/332] update go-vise. --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 16ccdc3..41c6700 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd go 1.23.0 require ( - git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9 + git.defalsify.org/vise.git v0.2.3-0.20250103172917-3e190a44568d 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 @@ -11,6 +11,7 @@ require ( 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 ) @@ -32,7 +33,6 @@ require ( github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/crypto v0.27.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/text v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9086cd8..6bef621 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9 h1:O3m+NgWDWtJm8OculT99c4bDMAO4xLe2c8hpCKpsd9g= -git.defalsify.org/vise.git v0.2.3-0.20241231085136-8582c7e157d9/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck= +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= 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= From 3ce14355911d12cde021f51e07b30cd986f3a6bb Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 4 Jan 2025 10:38:25 +0300 Subject: [PATCH 322/332] extract session id from africastalking request --- cmd/africastalking/main.go | 52 +++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index ca88978..e25813a 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "net/http" + "net/url" "os" "os/signal" "path" @@ -29,7 +30,7 @@ import ( ) var ( - logg = logging.NewVanilla() + logg = logging.NewVanilla().WithDomain("AfricasTalking").WithContextKey("at-session-id") scriptDir = path.Join("services", "registration") build = "dev" menuSeparator = ": " @@ -39,7 +40,43 @@ func init() { initializers.LoadEnvVariables() } -type atRequestParser struct{} +type atRequestParser struct { + context context.Context +} + +func parseQueryParams(query string) map[string]string { + params := make(map[string]string) + + queryParams := strings.Split(query, "&") + for _, param := range queryParams { + // Split each key-value pair by '=' + parts := strings.SplitN(param, "=", 2) + if len(parts) == 2 { + params[parts[0]] = parts[1] + } + } + return params +} + +func extractATSessionId(decodedStr string) (string, error) { + var data map[string]string + err := json.Unmarshal([]byte(decodedStr), &data) + + if err != nil { + logg.Errorf("Error unmarshalling JSON: %v", err) + return "", nil + } + decodedBody, err := url.QueryUnescape(data["body"]) + if err != nil { + logg.Errorf("Error URL-decoding body: %v", err) + return "", nil + } + params := parseQueryParams(decodedBody) + + sessionId := params["sessionId"] + return sessionId, nil + +} func (arp *atRequestParser) GetSessionId(rq any) (string, error) { rqv, ok := rq.(*http.Request) @@ -63,7 +100,12 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) { if err != nil { logg.Warnf("failed to marshal request body", "err", err) } else { - logg.Debugf("received request", "bytes", logBytes) + decodedStr := string(logBytes) + sessionId, err := extractATSessionId(decodedStr) + if err != nil { + context.WithValue(arp.context, "at-session-id", sessionId) + } + logg.Debugf("Received request:", decodedStr) } if err := rqv.ParseForm(); err != nil { @@ -191,7 +233,9 @@ func main() { } defer stateStore.Close() - rp := &atRequestParser{} + rp := &atRequestParser{ + context: ctx, + } bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl) sh := httpserver.NewATSessionHandler(bsh) From 62f3681b9e725ad10521c1f4957782a7de650159 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 4 Jan 2025 10:40:26 +0300 Subject: [PATCH 323/332] define context keysessionid using go-vise --withcontext --- internal/handlers/ussd/menuhandler.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 3919595..1cebba3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -28,7 +28,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("ussdmenuhandler") + logg = logging.NewVanilla().WithDomain("ussdmenuhandler").WithContextKey("session-id") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") ) @@ -122,9 +122,12 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource h.st.Code = []byte{} } - sessionId, _ := ctx.Value("SessionId").(string) - flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") + sessionId, ok := ctx.Value("SessionId").(string) + if ok { + context.WithValue(ctx, "session-id", sessionId) + } + flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") isAdmin, _ := h.adminstore.IsAdmin(sessionId) if isAdmin { From 3ed9caf16d3e903b72de8e967a34cd5013be6104 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 08:02:44 +0000 Subject: [PATCH 324/332] Factor out request parsers --- cmd/africastalking/main.go | 109 +--------------------------- internal/http/at/parse.go | 121 ++++++++++++++++++++++++++++++++ internal/http/at/server.go | 2 +- internal/http/at/server_test.go | 2 +- internal/http/parse.go | 38 ++++++++++ internal/http/server.go | 29 -------- 6 files changed, 162 insertions(+), 139 deletions(-) create mode 100644 internal/http/at/parse.go create mode 100644 internal/http/parse.go diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index a7fa74b..3ac1591 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -39,113 +39,6 @@ var ( func init() { initializers.LoadEnvVariables() } - -type atRequestParser struct { - context context.Context -} - -func parseQueryParams(query string) map[string]string { - params := make(map[string]string) - - queryParams := strings.Split(query, "&") - for _, param := range queryParams { - // Split each key-value pair by '=' - parts := strings.SplitN(param, "=", 2) - if len(parts) == 2 { - params[parts[0]] = parts[1] - } - } - return params -} - -func extractATSessionId(decodedStr string) (string, error) { - var data map[string]string - err := json.Unmarshal([]byte(decodedStr), &data) - - if err != nil { - logg.Errorf("Error unmarshalling JSON: %v", err) - return "", nil - } - decodedBody, err := url.QueryUnescape(data["body"]) - if err != nil { - logg.Errorf("Error URL-decoding body: %v", err) - return "", nil - } - params := parseQueryParams(decodedBody) - - sessionId := params["sessionId"] - return sessionId, nil - -} - -func (arp *atRequestParser) GetSessionId(rq any) (string, error) { - rqv, ok := rq.(*http.Request) - if !ok { - logg.Warnf("got an invalid request", "req", rq) - return "", handlers.ErrInvalidRequest - } - - // Capture body (if any) for logging - body, err := io.ReadAll(rqv.Body) - if err != nil { - logg.Warnf("failed to read request body", "err", err) - return "", fmt.Errorf("failed to read request body: %v", err) - } - // Reset the body for further reading - rqv.Body = io.NopCloser(bytes.NewReader(body)) - - // Log the body as JSON - bodyLog := map[string]string{"body": string(body)} - logBytes, err := json.Marshal(bodyLog) - if err != nil { - logg.Warnf("failed to marshal request body", "err", err) - } else { - decodedStr := string(logBytes) - sessionId, err := extractATSessionId(decodedStr) - if err != nil { - context.WithValue(arp.context, "at-session-id", sessionId) - } - logg.Debugf("Received request:", decodedStr) - } - - if err := rqv.ParseForm(); err != nil { - logg.Warnf("failed to parse form data", "err", err) - return "", fmt.Errorf("failed to parse form data: %v", err) - } - - phoneNumber := rqv.FormValue("phoneNumber") - if phoneNumber == "" { - return "", fmt.Errorf("no phone number found") - } - - formattedNumber, err := common.FormatPhoneNumber(phoneNumber) - if err != nil { - logg.Warnf("failed to format phone number", "err", err) - return "", fmt.Errorf("failed to format number") - } - - return formattedNumber, nil -} - -func (arp *atRequestParser) GetInput(rq any) ([]byte, error) { - rqv, ok := rq.(*http.Request) - if !ok { - return nil, handlers.ErrInvalidRequest - } - if err := rqv.ParseForm(); err != nil { - return nil, fmt.Errorf("failed to parse form data: %v", err) - } - - text := rqv.FormValue("text") - - parts := strings.Split(text, "*") - if len(parts) == 0 { - return nil, fmt.Errorf("no input found") - } - - return []byte(parts[len(parts)-1]), nil -} - func main() { config.LoadConfig() @@ -233,7 +126,7 @@ func main() { } defer stateStore.Close() - rp := &atRequestParser{ + rp := &at.ATRequestParser{ context: ctx, } bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl) diff --git a/internal/http/at/parse.go b/internal/http/at/parse.go new file mode 100644 index 0000000..a40cf0f --- /dev/null +++ b/internal/http/at/parse.go @@ -0,0 +1,121 @@ +package at + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "git.grassecon.net/urdt/ussd/common" + "git.grassecon.net/urdt/ussd/internal/handlers" +) + +type ATRequestParser struct { + context context.Context +} + +func (arp *ATRequestParser) GetSessionId(rq any) (string, error) { + rqv, ok := rq.(*http.Request) + if !ok { + logg.Warnf("got an invalid request", "req", rq) + return "", handlers.ErrInvalidRequest + } + + // Capture body (if any) for logging + body, err := io.ReadAll(rqv.Body) + if err != nil { + logg.Warnf("failed to read request body", "err", err) + return "", fmt.Errorf("failed to read request body: %v", err) + } + // Reset the body for further reading + rqv.Body = io.NopCloser(bytes.NewReader(body)) + + // Log the body as JSON + bodyLog := map[string]string{"body": string(body)} + logBytes, err := json.Marshal(bodyLog) + if err != nil { + logg.Warnf("failed to marshal request body", "err", err) + } else { + decodedStr := string(logBytes) + sessionId, err := extractATSessionId(decodedStr) + if err != nil { + context.WithValue(arp.context, "at-session-id", sessionId) + } + logg.Debugf("Received request:", decodedStr) + } + + if err := rqv.ParseForm(); err != nil { + logg.Warnf("failed to parse form data", "err", err) + return "", fmt.Errorf("failed to parse form data: %v", err) + } + + phoneNumber := rqv.FormValue("phoneNumber") + if phoneNumber == "" { + return "", fmt.Errorf("no phone number found") + } + + formattedNumber, err := common.FormatPhoneNumber(phoneNumber) + if err != nil { + logg.Warnf("failed to format phone number", "err", err) + return "", fmt.Errorf("failed to format number") + } + + return formattedNumber, nil +} + +func (arp *ATRequestParser) GetInput(rq any) ([]byte, error) { + rqv, ok := rq.(*http.Request) + if !ok { + return nil, handlers.ErrInvalidRequest + } + if err := rqv.ParseForm(); err != nil { + return nil, fmt.Errorf("failed to parse form data: %v", err) + } + + text := rqv.FormValue("text") + + parts := strings.Split(text, "*") + if len(parts) == 0 { + return nil, fmt.Errorf("no input found") + } + + return []byte(parts[len(parts)-1]), nil +} + +func parseQueryParams(query string) map[string]string { + params := make(map[string]string) + + queryParams := strings.Split(query, "&") + for _, param := range queryParams { + // Split each key-value pair by '=' + parts := strings.SplitN(param, "=", 2) + if len(parts) == 2 { + params[parts[0]] = parts[1] + } + } + return params +} + +func extractATSessionId(decodedStr string) (string, error) { + var data map[string]string + err := json.Unmarshal([]byte(decodedStr), &data) + + if err != nil { + logg.Errorf("Error unmarshalling JSON: %v", err) + return "", nil + } + decodedBody, err := url.QueryUnescape(data["body"]) + if err != nil { + logg.Errorf("Error URL-decoding body: %v", err) + return "", nil + } + params := parseQueryParams(decodedBody) + + sessionId := params["sessionId"] + return sessionId, nil + +} diff --git a/internal/http/at/server.go b/internal/http/at/server.go index 9cade3d..705ff76 100644 --- a/internal/http/at/server.go +++ b/internal/http/at/server.go @@ -1,4 +1,4 @@ -package http +package at import ( "io" diff --git a/internal/http/at/server_test.go b/internal/http/at/server_test.go index d49f9ce..dd45c25 100644 --- a/internal/http/at/server_test.go +++ b/internal/http/at/server_test.go @@ -1,4 +1,4 @@ -package http +package at import ( "context" diff --git a/internal/http/parse.go b/internal/http/parse.go new file mode 100644 index 0000000..ec8e00b --- /dev/null +++ b/internal/http/parse.go @@ -0,0 +1,38 @@ +package http + +import ( + "io/ioutil" + "net/http" + + "git.grassecon.net/urdt/ussd/internal/handlers" +) + +type DefaultRequestParser struct { +} + +func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { + rqv, ok := rq.(*http.Request) + if !ok { + return "", handlers.ErrInvalidRequest + } + v := rqv.Header.Get("X-Vise-Session") + if v == "" { + return "", handlers.ErrSessionMissing + } + return v, nil +} + +func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) { + rqv, ok := rq.(*http.Request) + if !ok { + return nil, handlers.ErrInvalidRequest + } + defer rqv.Body.Close() + v, err := ioutil.ReadAll(rqv.Body) + if err != nil { + return nil, err + } + return v, nil +} + + diff --git a/internal/http/server.go b/internal/http/server.go index df15407..9cadfa3 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -1,7 +1,6 @@ package http import ( - "io/ioutil" "net/http" "strconv" @@ -14,34 +13,6 @@ var ( logg = logging.NewVanilla().WithDomain("httpserver") ) -type DefaultRequestParser struct { -} - -func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { - rqv, ok := rq.(*http.Request) - if !ok { - return "", handlers.ErrInvalidRequest - } - v := rqv.Header.Get("X-Vise-Session") - if v == "" { - return "", handlers.ErrSessionMissing - } - return v, nil -} - -func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) { - rqv, ok := rq.(*http.Request) - if !ok { - return nil, handlers.ErrInvalidRequest - } - defer rqv.Body.Close() - v, err := ioutil.ReadAll(rqv.Body) - if err != nil { - return nil, err - } - return v, nil -} - type SessionHandler struct { handlers.RequestHandler } From bcb3ab905e789b9213e492bd8de21c3f36aaf71b Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 08:09:18 +0000 Subject: [PATCH 325/332] Move db related to own package --- internal/storage/{ => db}/gdbm/gdbm.go | 0 internal/storage/{ => db}/sub_prefix_db.go | 0 internal/storage/{ => db}/sub_prefix_db_test.go | 0 internal/storage/storageservice.go | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename internal/storage/{ => db}/gdbm/gdbm.go (100%) rename internal/storage/{ => db}/sub_prefix_db.go (100%) rename internal/storage/{ => db}/sub_prefix_db_test.go (100%) diff --git a/internal/storage/gdbm/gdbm.go b/internal/storage/db/gdbm/gdbm.go similarity index 100% rename from internal/storage/gdbm/gdbm.go rename to internal/storage/db/gdbm/gdbm.go diff --git a/internal/storage/sub_prefix_db.go b/internal/storage/db/sub_prefix_db.go similarity index 100% rename from internal/storage/sub_prefix_db.go rename to internal/storage/db/sub_prefix_db.go diff --git a/internal/storage/sub_prefix_db_test.go b/internal/storage/db/sub_prefix_db_test.go similarity index 100% rename from internal/storage/sub_prefix_db_test.go rename to internal/storage/db/sub_prefix_db_test.go diff --git a/internal/storage/storageservice.go b/internal/storage/storageservice.go index 7856095..04e75ce 100644 --- a/internal/storage/storageservice.go +++ b/internal/storage/storageservice.go @@ -13,7 +13,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/initializers" - gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/gdbm" + gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/db/gdbm" ) var ( From d950b10b50f59c91732f4ad2f1f7ca42f0f69ef7 Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 08:37:28 +0000 Subject: [PATCH 326/332] Move prefix db spec to separate package --- cmd/africastalking/main.go | 5 ----- common/storage.go | 5 +++-- common/transfer_statements.go | 4 ++-- common/vouchers.go | 4 ++-- internal/handlers/ussd/menuhandler.go | 6 +++--- internal/handlers/ussd/menuhandler_test.go | 6 +++--- 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 3ac1591..2696b51 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -1,19 +1,14 @@ package main import ( - "bytes" "context" - "encoding/json" "flag" "fmt" - "io" "net/http" - "net/url" "os" "os/signal" "path" "strconv" - "strings" "syscall" "git.defalsify.org/vise.git/engine" diff --git a/common/storage.go b/common/storage.go index dff4774..d37bce3 100644 --- a/common/storage.go +++ b/common/storage.go @@ -8,14 +8,15 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/persist" "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" ) func StoreToDb(store *UserDataStore) db.Db { return store.Db } -func StoreToPrefixDb(store *UserDataStore, pfx []byte) storage.PrefixDb { - return storage.NewSubPrefixDb(store.Db, pfx) +func StoreToPrefixDb(store *UserDataStore, pfx []byte) dbstorage.PrefixDb { + return dbstorage.NewSubPrefixDb(store.Db, pfx) } type StorageServices interface { diff --git a/common/transfer_statements.go b/common/transfer_statements.go index 243ef4c..e97437f 100644 --- a/common/transfer_statements.go +++ b/common/transfer_statements.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -56,7 +56,7 @@ func ProcessTransfers(transfers []dataserviceapi.Last10TxResponse) TransferMetad // GetTransferData retrieves and matches transfer data // returns a formatted string of the full transaction/statement -func GetTransferData(ctx context.Context, db storage.PrefixDb, publicKey string, index int) (string, error) { +func GetTransferData(ctx context.Context, db dbstorage.PrefixDb, publicKey string, index int) (string, error) { keys := []DataTyp{DATA_TX_SENDERS, DATA_TX_RECIPIENTS, DATA_TX_VALUES, DATA_TX_ADDRESSES, DATA_TX_HASHES, DATA_TX_DATES, DATA_TX_SYMBOLS} data := make(map[DataTyp]string) diff --git a/common/vouchers.go b/common/vouchers.go index 6cff91d..5dbdb71 100644 --- a/common/vouchers.go +++ b/common/vouchers.go @@ -6,7 +6,7 @@ import ( "math/big" "strings" - "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -63,7 +63,7 @@ func ScaleDownBalance(balance, decimals string) string { } // GetVoucherData retrieves and matches voucher data -func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { +func GetVoucherData(ctx context.Context, db dbstorage.PrefixDb, input string) (*dataserviceapi.TokenHoldings, error) { keys := []DataTyp{DATA_VOUCHER_SYMBOLS, DATA_VOUCHER_BALANCES, DATA_VOUCHER_DECIMALS, DATA_VOUCHER_ADDRESSES} data := make(map[DataTyp]string) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 1cebba3..095d77b 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -23,7 +23,7 @@ import ( "git.grassecon.net/urdt/ussd/remote" "gopkg.in/leonelquinteros/gotext.v1" - "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -64,7 +64,7 @@ type Handlers struct { adminstore *utils.AdminStore flagManager *asm.FlagParser accountService remote.AccountServiceInterface - prefixDb storage.PrefixDb + prefixDb dbstorage.PrefixDb profile *models.Profile ReplaceSeparatorFunc func(string) string } @@ -80,7 +80,7 @@ func NewHandlers(appFlags *asm.FlagParser, userdataStore db.Db, adminstore *util // Instantiate the SubPrefixDb with "DATATYPE_USERDATA" prefix prefix := common.ToBytes(db.DATATYPE_USERDATA) - prefixDb := storage.NewSubPrefixDb(userdataStore, prefix) + prefixDb := dbstorage.NewSubPrefixDb(userdataStore, prefix) h := &Handlers{ userdataStore: userDb, diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 12ed5c2..914dffc 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -13,7 +13,7 @@ import ( "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" - "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" "git.grassecon.net/urdt/ussd/internal/testutil/mocks" "git.grassecon.net/urdt/ussd/internal/testutil/testservice" "git.grassecon.net/urdt/ussd/internal/utils" @@ -59,14 +59,14 @@ func InitializeTestStore(t *testing.T) (context.Context, *common.UserDataStore) return ctx, store } -func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *storage.SubPrefixDb { +func InitializeTestSubPrefixDb(t *testing.T, ctx context.Context) *dbstorage.SubPrefixDb { db := memdb.NewMemDb() err := db.Connect(ctx, "") if err != nil { t.Fatal(err) } prefix := common.ToBytes(visedb.DATATYPE_USERDATA) - spdb := storage.NewSubPrefixDb(db, prefix) + spdb := dbstorage.NewSubPrefixDb(db, prefix) return spdb } From d5a2680500cabfc2533db48ab7289efbeb491315 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 4 Jan 2025 12:02:45 +0300 Subject: [PATCH 327/332] make context accessible --- cmd/africastalking/main.go | 4 ++-- internal/http/at/parse.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 2696b51..0019239 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -15,10 +15,10 @@ import ( "git.defalsify.org/vise.git/logging" "git.defalsify.org/vise.git/resource" - "git.grassecon.net/urdt/ussd/common" "git.grassecon.net/urdt/ussd/config" "git.grassecon.net/urdt/ussd/initializers" "git.grassecon.net/urdt/ussd/internal/handlers" + "git.grassecon.net/urdt/ussd/internal/http/at" httpserver "git.grassecon.net/urdt/ussd/internal/http/at" "git.grassecon.net/urdt/ussd/internal/storage" "git.grassecon.net/urdt/ussd/remote" @@ -122,7 +122,7 @@ func main() { defer stateStore.Close() rp := &at.ATRequestParser{ - context: ctx, + Context: ctx, } bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl) sh := httpserver.NewATSessionHandler(bsh) diff --git a/internal/http/at/parse.go b/internal/http/at/parse.go index a40cf0f..d2696ed 100644 --- a/internal/http/at/parse.go +++ b/internal/http/at/parse.go @@ -15,7 +15,7 @@ import ( ) type ATRequestParser struct { - context context.Context + Context context.Context } func (arp *ATRequestParser) GetSessionId(rq any) (string, error) { @@ -43,7 +43,7 @@ func (arp *ATRequestParser) GetSessionId(rq any) (string, error) { decodedStr := string(logBytes) sessionId, err := extractATSessionId(decodedStr) if err != nil { - context.WithValue(arp.context, "at-session-id", sessionId) + context.WithValue(arp.Context, "at-session-id", sessionId) } logg.Debugf("Received request:", decodedStr) } From 25867cf05ea1ba3da5ebbf8e8b7deacb5ca3208d Mon Sep 17 00:00:00 2001 From: lash Date: Sat, 4 Jan 2025 09:42:36 +0000 Subject: [PATCH 328/332] Rehabilitate voucher test --- common/vouchers_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/vouchers_test.go b/common/vouchers_test.go index ba6cd60..8b04e4a 100644 --- a/common/vouchers_test.go +++ b/common/vouchers_test.go @@ -10,7 +10,7 @@ import ( visedb "git.defalsify.org/vise.git/db" memdb "git.defalsify.org/vise.git/db/mem" - "git.grassecon.net/urdt/ussd/internal/storage" + dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db" dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api" ) @@ -86,7 +86,7 @@ func TestGetVoucherData(t *testing.T) { } prefix := ToBytes(visedb.DATATYPE_USERDATA) - spdb := storage.NewSubPrefixDb(db, prefix) + spdb := dbstorage.NewSubPrefixDb(db, prefix) // Test voucher data mockData := map[DataTyp][]byte{ From 974af6b2a75a90bd6c05ed66d9632edd5c1ceae1 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 6 Jan 2025 08:50:53 +0300 Subject: [PATCH 329/332] pass context as an argument --- cmd/africastalking/main.go | 4 +-- cmd/async/main.go | 6 ++-- internal/handlers/single.go | 30 +++++++++---------- internal/http/parse.go | 5 ++-- internal/http/server_test.go | 3 +- .../mocks/httpmocks/requestparsermock.go | 4 ++- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go index 0019239..dfcaca1 100644 --- a/cmd/africastalking/main.go +++ b/cmd/africastalking/main.go @@ -121,9 +121,7 @@ func main() { } defer stateStore.Close() - rp := &at.ATRequestParser{ - Context: ctx, - } + rp := &at.ATRequestParser{} bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl) sh := httpserver.NewATSessionHandler(bsh) diff --git a/cmd/async/main.go b/cmd/async/main.go index 9cd04b3..bf23d9f 100644 --- a/cmd/async/main.go +++ b/cmd/async/main.go @@ -21,8 +21,8 @@ import ( ) var ( - logg = logging.NewVanilla() - scriptDir = path.Join("services", "registration") + logg = logging.NewVanilla() + scriptDir = path.Join("services", "registration") menuSeparator = ": " ) @@ -35,7 +35,7 @@ type asyncRequestParser struct { input []byte } -func (p *asyncRequestParser) GetSessionId(r any) (string, error) { +func (p *asyncRequestParser) GetSessionId(ctx context.Context, r any) (string, error) { return p.sessionId, nil } diff --git a/internal/handlers/single.go b/internal/handlers/single.go index 6929617..1b11a64 100644 --- a/internal/handlers/single.go +++ b/internal/handlers/single.go @@ -6,9 +6,9 @@ import ( "io" "git.defalsify.org/vise.git/engine" - "git.defalsify.org/vise.git/resource" - "git.defalsify.org/vise.git/persist" "git.defalsify.org/vise.git/logging" + "git.defalsify.org/vise.git/persist" + "git.defalsify.org/vise.git/resource" "git.grassecon.net/urdt/ussd/internal/storage" ) @@ -20,33 +20,33 @@ var ( 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") + 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") ) type RequestSession struct { - Ctx context.Context - Config engine.Config - Engine engine.Engine - Input []byte - Storage *storage.Storage - Writer io.Writer + 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(rq any) (string, error) + GetSessionId(context 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 + 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) diff --git a/internal/http/parse.go b/internal/http/parse.go index ec8e00b..b4e784d 100644 --- a/internal/http/parse.go +++ b/internal/http/parse.go @@ -1,6 +1,7 @@ package http import ( + "context" "io/ioutil" "net/http" @@ -10,7 +11,7 @@ import ( type DefaultRequestParser struct { } -func (rp *DefaultRequestParser) GetSessionId(rq any) (string, error) { +func (rp *DefaultRequestParser) GetSessionId(ctx context.Context, rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { return "", handlers.ErrInvalidRequest @@ -34,5 +35,3 @@ func (rp *DefaultRequestParser) GetInput(rq any) ([]byte, error) { } return v, nil } - - diff --git a/internal/http/server_test.go b/internal/http/server_test.go index a46f98e..23afd5d 100644 --- a/internal/http/server_test.go +++ b/internal/http/server_test.go @@ -2,6 +2,7 @@ package http import ( "bytes" + "context" "errors" "net/http" "net/http/httptest" @@ -161,7 +162,7 @@ func TestDefaultRequestParser_GetSessionId(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - id, err := parser.GetSessionId(tt.request) + id, err := parser.GetSessionId(context.Background(),tt.request) if id != tt.expectedID { t.Errorf("Expected session ID %s, got %s", tt.expectedID, id) diff --git a/internal/testutil/mocks/httpmocks/requestparsermock.go b/internal/testutil/mocks/httpmocks/requestparsermock.go index 54b16bf..3c19e12 100644 --- a/internal/testutil/mocks/httpmocks/requestparsermock.go +++ b/internal/testutil/mocks/httpmocks/requestparsermock.go @@ -1,12 +1,14 @@ package httpmocks +import "context" + // MockRequestParser implements the handlers.RequestParser interface for testing type MockRequestParser struct { GetSessionIdFunc func(any) (string, error) GetInputFunc func(any) ([]byte, error) } -func (m *MockRequestParser) GetSessionId(rq any) (string, error) { +func (m *MockRequestParser) GetSessionId(ctx context.Context, rq any) (string, error) { return m.GetSessionIdFunc(rq) } From c69d3896f1a932af9be30390fbbea514817893b9 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 6 Jan 2025 08:52:53 +0300 Subject: [PATCH 330/332] pass context as an argument,rename context keys --- internal/handlers/ussd/menuhandler.go | 4 ++-- internal/http/at/parse.go | 8 +++----- internal/http/at/server.go | 6 +++--- internal/http/server.go | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 095d77b..dfdbd02 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -28,7 +28,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("ussdmenuhandler").WithContextKey("session-id") + logg = logging.NewVanilla().WithDomain("ussdmenuhandler").WithContextKey("SessionId") scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") ) @@ -124,7 +124,7 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource sessionId, ok := ctx.Value("SessionId").(string) if ok { - context.WithValue(ctx, "session-id", sessionId) + ctx = context.WithValue(ctx, "SessionId", sessionId) } flag_admin_privilege, _ := h.flagManager.GetFlag("flag_admin_privilege") diff --git a/internal/http/at/parse.go b/internal/http/at/parse.go index d2696ed..5f27d50 100644 --- a/internal/http/at/parse.go +++ b/internal/http/at/parse.go @@ -15,16 +15,14 @@ import ( ) type ATRequestParser struct { - Context context.Context } -func (arp *ATRequestParser) GetSessionId(rq any) (string, error) { +func (arp *ATRequestParser) GetSessionId(ctx context.Context, rq any) (string, error) { rqv, ok := rq.(*http.Request) if !ok { logg.Warnf("got an invalid request", "req", rq) return "", handlers.ErrInvalidRequest } - // Capture body (if any) for logging body, err := io.ReadAll(rqv.Body) if err != nil { @@ -43,9 +41,9 @@ func (arp *ATRequestParser) GetSessionId(rq any) (string, error) { decodedStr := string(logBytes) sessionId, err := extractATSessionId(decodedStr) if err != nil { - context.WithValue(arp.Context, "at-session-id", sessionId) + ctx = context.WithValue(ctx, "AT-SessionId", sessionId) } - logg.Debugf("Received request:", decodedStr) + logg.DebugCtxf(ctx, "Received request:", decodedStr) } if err := rqv.ParseForm(); err != nil { diff --git a/internal/http/at/server.go b/internal/http/at/server.go index 705ff76..92f76bc 100644 --- a/internal/http/at/server.go +++ b/internal/http/at/server.go @@ -10,7 +10,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("atserver") + logg = logging.NewVanilla().WithDomain("atserver").WithContextKey("AT-SessionId") ) type ATSessionHandler struct { @@ -34,7 +34,7 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) rp := ash.GetRequestParser() cfg := ash.GetConfig() - cfg.SessionId, err = rp.GetSessionId(req) + cfg.SessionId, err = rp.GetSessionId(req.Context(), req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) ash.WriteError(w, 400, err) @@ -48,7 +48,7 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) return } - rqs, err = ash.Process(rqs) + rqs, err = ash.Process(rqs) switch err { case nil: // set code to 200 if no err code = 200 diff --git a/internal/http/server.go b/internal/http/server.go index 9cadfa3..92855b9 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -10,7 +10,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("httpserver") + logg = logging.NewVanilla().WithDomain("httpserver").WithContextKey("at-session-id") ) type SessionHandler struct { @@ -46,7 +46,7 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { rp := f.GetRequestParser() cfg := f.GetConfig() - cfg.SessionId, err = rp.GetSessionId(req) + cfg.SessionId, err = rp.GetSessionId(req.Context(), req) if err != nil { logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err) f.WriteError(w, 400, err) From 362eb209efd81973045abf9deaa1b9e7370c6848 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 6 Jan 2025 09:54:28 +0300 Subject: [PATCH 331/332] add SessionId to context key --- internal/http/at/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/at/server.go b/internal/http/at/server.go index 92f76bc..3399dd5 100644 --- a/internal/http/at/server.go +++ b/internal/http/at/server.go @@ -10,7 +10,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("atserver").WithContextKey("AT-SessionId") + logg = logging.NewVanilla().WithDomain("atserver").WithContextKey("SessionId").WithContextKey("AT-SessionId") ) type ATSessionHandler struct { From 44570e20ef5fbde511d79f9b4a3f694478f14dcd Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 6 Jan 2025 09:59:47 +0300 Subject: [PATCH 332/332] remove unused context key :- at-session-id --- internal/http/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/http/server.go b/internal/http/server.go index 92855b9..0a2533e 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -10,7 +10,7 @@ import ( ) var ( - logg = logging.NewVanilla().WithDomain("httpserver").WithContextKey("at-session-id") + logg = logging.NewVanilla().WithDomain("httpserver") ) type SessionHandler struct {