From cc323707fc77e70c6e99d5236ae1bac60f90f6cf Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 10:21:20 +0300 Subject: [PATCH 01/37] setup parser --- cmd/main.go | 6 +++++- internal/handlers/ussd/menuhandler.go | 24 +++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 2daee8c..39136a9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -83,7 +83,11 @@ func main() { fp := path.Join(dp, sessionId) - ussdHandlers := ussd.NewHandlers(fp, &st) + ussdHandlers,err := ussd.NewHandlers(fp, &st) + + if(err != nil){ + fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err) + } rfs.AddLocalFunc("select_language", ussdHandlers.SetLanguage) rfs.AddLocalFunc("create_account", ussdHandlers.CreateAccount) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 4eb2a83..f5776f1 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/lang" "git.defalsify.org/vise.git/resource" @@ -29,26 +30,31 @@ type FSData struct { St *state.State } - type Handlers struct { fs *FSData + parser *asm.FlagParser accountFileHandler utils.AccountFileHandlerInterface accountService server.AccountServiceInterface } -func NewHandlers(path string, st *state.State) *Handlers { +func NewHandlers(dir string, st *state.State) (*Handlers, error) { + pfp := path.Join(dir, "pp.csv") + parser := asm.NewFlagParser() + _, err := parser.Load(pfp) + if err != nil { + return nil, err + } return &Handlers{ fs: &FSData{ - Path: path, + Path: dir, St: st, }, - accountFileHandler: utils.NewAccountFileHandler(path + "_data"), + parser: parser, + accountFileHandler: utils.NewAccountFileHandler(dir + "_data"), accountService: &server.AccountService{}, - } + }, nil } - - // Define the regex pattern as a constant const pinPattern = `^\d{4}$` @@ -191,7 +197,7 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res return res, nil } -//codeFromCtx retrieves language codes from the context that can be used for handling translations +// codeFromCtx retrieves language codes from the context that can be used for handling translations func codeFromCtx(ctx context.Context) string { var code string engine.Logg.DebugCtxf(ctx, "in msg", "ctx", ctx, "val", code) @@ -446,7 +452,7 @@ func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - + res.Content = l.Get("Thank you for using Sarafu. Goodbye!") res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) return res, nil From de4aaeb02b15aecb79e581460c5aef97e8712ade Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 10:26:49 +0300 Subject: [PATCH 02/37] use script dir for parser --- 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 f5776f1..91f4a4e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -38,7 +38,7 @@ type Handlers struct { } func NewHandlers(dir string, st *state.State) (*Handlers, error) { - pfp := path.Join(dir, "pp.csv") + pfp := path.Join(scriptDir, "pp.csv") parser := asm.NewFlagParser() _, err := parser.Load(pfp) if err != nil { From 828f9a17fc749336976243745b3ecc7ebed849fb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 10:28:11 +0300 Subject: [PATCH 03/37] Added the csv containing string flags --- services/registration/pp.csv | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 services/registration/pp.csv diff --git a/services/registration/pp.csv b/services/registration/pp.csv new file mode 100644 index 0000000..e9626cf --- /dev/null +++ b/services/registration/pp.csv @@ -0,0 +1,16 @@ +flag,language_set,8,checks whether the user has set their prefered language +flag,account_created,9,this is set when an account has been created on the API +flag,account_pending,10,this is set when an account does not have a status of SUCCESS +flag,account_success,11,this is set when an account has a status of SUCCESS +flag,account_authorized,12,this is set to allow a user access guarded nodes after providing a correct PIN +flag,invalid_recipient,13,this is set when the transaction recipient is invalid +flag,invalid_recipient_with_invite,14,this is set when the transaction recipient is valid but not on the platform +flag,incorrect_pin,15,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,allow_update,16,this is set to allow a user to update their profile data +flag,invalid_amount,17,this is set when the given transaction amount is invalid +flag,pin_set,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu +flag,valid_pin,19,this is set when the given PIN is valid +flag,pin_mismatch,20,this is set when the confirmation PIN matches the initial PIN during registration +flag,incorrect_date_format,21,this is set when the given year of birth is invalid +flag,account_creation_failed,22,this is set when there's an error from the API during account creation +flag,single_edit,23,this is set to allow a user to edit a single profile item such as familyName From bcc73dec2353fc66c322c4a10929be45c525c29f Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 11:27:47 +0300 Subject: [PATCH 04/37] Add the flag file during build --- services/registration/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/Makefile b/services/registration/Makefile index 578ebc6..1863b0b 100644 --- a/services/registration/Makefile +++ b/services/registration/Makefile @@ -4,7 +4,7 @@ TXTS = $(wildcard ./*.txt.orig) # Rule to build .bin files from .vis files %.vis: - go run ../../go-vise/dev/asm $(basename $@).vis > $(basename $@).bin + go run ../../go-vise/dev/asm -f pp.csv $(basename $@).vis > $(basename $@).bin @echo "Built $(basename $@).bin from $(basename $@).vis" # Rule to copy .orig files to .txt From ec4201139a783e11f1859ec8927c86009597e115 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 11:35:29 +0300 Subject: [PATCH 05/37] add _flag suffix --- services/registration/pp.csv | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index e9626cf..538870a 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -1,16 +1,16 @@ -flag,language_set,8,checks whether the user has set their prefered language -flag,account_created,9,this is set when an account has been created on the API -flag,account_pending,10,this is set when an account does not have a status of SUCCESS -flag,account_success,11,this is set when an account has a status of SUCCESS -flag,account_authorized,12,this is set to allow a user access guarded nodes after providing a correct PIN -flag,invalid_recipient,13,this is set when the transaction recipient is invalid -flag,invalid_recipient_with_invite,14,this is set when the transaction recipient is valid but not on the platform -flag,incorrect_pin,15,this is set when the provided PIN is invalid or does not match the current account's PIN -flag,allow_update,16,this is set to allow a user to update their profile data -flag,invalid_amount,17,this is set when the given transaction amount is invalid -flag,pin_set,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu -flag,valid_pin,19,this is set when the given PIN is valid -flag,pin_mismatch,20,this is set when the confirmation PIN matches the initial PIN during registration -flag,incorrect_date_format,21,this is set when the given year of birth is invalid -flag,account_creation_failed,22,this is set when there's an error from the API during account creation -flag,single_edit,23,this is set to allow a user to edit a single profile item such as familyName +flag,language_set_flag,8,checks whether the user has set their prefered language +flag,account_created_flag,9,this is set when an account has been created on the API +flag,account_pending_flag,10,this is set when an account does not have a status of SUCCESS +flag,account_success_flag,11,this is set when an account has a status of SUCCESS +flag,account_authorized_flag,12,this is set to allow a user access guarded nodes after providing a correct PIN +flag,invalid_recipient_flag,13,this is set when the transaction recipient is invalid +flag,invalid_recipient_with_invite_flag,14,this is set when the transaction recipient is valid but not on the platform +flag,incorrect_pin_flag,15,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,allow_update_flag,16,this is set to allow a user to update their profile data +flag,invalid_amount_flag,17,this is set when the given transaction amount is invalid +flag,pin_set_flag,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu +flag,valid_pin_flag,19,this is set when the given PIN is valid +flag,pin_mismatch_flag,20,this is set when the confirmation PIN matches the initial PIN during registration +flag,incorrect_date_format_flag,21,this is set when the given year of birth is invalid +flag,account_creation_failed_flag,22,this is set when there's an error from the API during account creation +flag,single_edit_flag,23,this is set to allow a user to edit a single profile item such as familyName From 9fb3ef8e2a5fbb6a715f865fd721f299a18b7e03 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 11:56:36 +0300 Subject: [PATCH 06/37] Use preprocessor flags --- services/registration/account_creation.vis | 2 +- services/registration/account_pending.vis | 2 +- services/registration/amount.vis | 2 +- services/registration/create_pin.vis | 4 ++-- services/registration/send.vis | 2 +- services/registration/transaction_pin.vis | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/services/registration/account_creation.vis b/services/registration/account_creation.vis index e3ecebb..1aca453 100644 --- a/services/registration/account_creation.vis +++ b/services/registration/account_creation.vis @@ -1,4 +1,4 @@ RELOAD verify_pin -CATCH create_pin_mismatch 20 1 +CATCH create_pin_mismatch pin_mismatch_flag 1 LOAD quit 0 HALT diff --git a/services/registration/account_pending.vis b/services/registration/account_pending.vis index 19e308c..a4baae5 100644 --- a/services/registration/account_pending.vis +++ b/services/registration/account_pending.vis @@ -1,3 +1,3 @@ RELOAD check_account_status -CATCH main 11 1 +CATCH main account_success_flag 1 HALT diff --git a/services/registration/amount.vis b/services/registration/amount.vis index 884c8a5..e134735 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -5,7 +5,7 @@ MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount -CATCH invalid_amount 17 1 +CATCH invalid_amount invalid_amount_flag 1 INCMP _ 0 LOAD get_recipient 12 LOAD get_sender 64 diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index 4994863..2755b1d 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -1,9 +1,9 @@ LOAD create_account 0 -CATCH account_creation_failed 22 1 +CATCH account_creation_failed account_creation_failed_flag 1 MOUT exit 0 HALT LOAD save_pin 0 RELOAD save_pin -CATCH . 15 1 +CATCH . incorrect_pin_flag 1 INCMP quit 0 INCMP confirm_create_pin * diff --git a/services/registration/send.vis b/services/registration/send.vis index d0fe211..5db5287 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -3,6 +3,6 @@ MOUT back 0 HALT LOAD validate_recipient 20 RELOAD validate_recipient -CATCH invalid_recipient 13 1 +CATCH invalid_recipient invalid_recipient_flag 1 INCMP _ 0 INCMP amount * diff --git a/services/registration/transaction_pin.vis b/services/registration/transaction_pin.vis index 5113b6c..19e0fcc 100644 --- a/services/registration/transaction_pin.vis +++ b/services/registration/transaction_pin.vis @@ -8,7 +8,7 @@ MOUT quit 9 HALT LOAD authorize_account 1 RELOAD authorize_account -CATCH incorrect_pin 15 1 +CATCH incorrect_pin incorrect_pin_flag 1 INCMP _ 0 INCMP quit 9 MOVE transaction_initiated From 4e69fa454d4b0df6603f8a987ffa9c42b3d7063c Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 12:02:44 +0300 Subject: [PATCH 07/37] use string flags --- services/registration/community_balance.vis | 4 ++-- services/registration/enter_location.vis | 4 ++-- services/registration/enter_offerings.vis | 4 ++-- services/registration/enter_yob.vis | 4 ++-- services/registration/my_balance.vis | 4 ++-- services/registration/select_gender.vis | 4 ++-- services/registration/view_profile.vis | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index 4894944..c25ced3 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +CATCH incorrect_pin incorrect_pin_flag 1 +CATCH pin_entry account_authorized_flag 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index f6ccf77..6d9bc03 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,11 +1,11 @@ CATCH incorrect_date_format 21 1 LOAD save_yob 0 -CATCH update_success 16 1 +CATCH update_success allow_update_flag 1 MOUT back 0 HALT INCMP _ 0 LOAD save_location 0 -CATCH pin_entry 23 1 +CATCH pin_entry single_edit_flag 1 INCMP enter_offerings * diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis index f48f1ea..9bc4407 100644 --- a/services/registration/enter_offerings.vis +++ b/services/registration/enter_offerings.vis @@ -1,6 +1,6 @@ LOAD save_location 0 -CATCH incorrect_pin 15 1 -CATCH update_success 16 1 +CATCH incorrect_pin incorrect_pin_flag 1 +CATCH update_success allow_update_flag 1 MOUT back 0 HALT LOAD save_offerings 0 diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis index 3b3f0d2..46a3771 100644 --- a/services/registration/enter_yob.vis +++ b/services/registration/enter_yob.vis @@ -1,9 +1,9 @@ LOAD save_gender 0 -CATCH update_success 16 1 +CATCH update_success allow_update_flag 1 MOUT back 0 HALT INCMP _ 0 LOAD verify_yob 8 LOAD save_yob 0 -CATCH pin_entry 23 1 +CATCH pin_entry single_edit_flag 1 INCMP enter_location * diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index 4894944..c25ced3 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +CATCH incorrect_pin incorrect_pin_flag 1 +CATCH pin_entry account_authorized_flag 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index 3029406..1159c64 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -1,12 +1,12 @@ LOAD save_familyname 0 -CATCH update_success 16 1 +CATCH update_success allow_update_flag 1 MOUT male 1 MOUT female 2 MOUT unspecified 3 MOUT back 0 HALT LOAD save_gender 0 -CATCH pin_entry 23 1 +CATCH pin_entry single_edit_flag 1 INCMP _ 0 INCMP enter_yob 1 INCMP enter_yob 2 diff --git a/services/registration/view_profile.vis b/services/registration/view_profile.vis index 8bf2b76..bf1196b 100644 --- a/services/registration/view_profile.vis +++ b/services/registration/view_profile.vis @@ -1,8 +1,8 @@ LOAD get_profile_info 0 MAP get_profile_info LOAD reset_incorrect 0 -CATCH incorrect_pin 15 1 -CATCH pin_entry 12 0 +CATCH incorrect_pin incorrect_pin_flag 1 +CATCH pin_entry account_authorized_flag 0 MOUT back 0 HALT INCMP _ 0 From 26959be149949d2dc16e1451cbdaa19996777099 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 12:07:46 +0300 Subject: [PATCH 08/37] Fixed bug on transaction pin node --- services/registration/transaction_pin.vis | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/registration/transaction_pin.vis b/services/registration/transaction_pin.vis index 19e0fcc..5fcf2e7 100644 --- a/services/registration/transaction_pin.vis +++ b/services/registration/transaction_pin.vis @@ -11,5 +11,4 @@ RELOAD authorize_account CATCH incorrect_pin incorrect_pin_flag 1 INCMP _ 0 INCMP quit 9 -MOVE transaction_initiated - +INCMP transaction_initiated * From 42ab2dd577ebc0aabbc01a99fc5c0f6d18f59c99 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 12:16:36 +0300 Subject: [PATCH 09/37] Use preprocessor flags --- services/registration/enter_location.vis | 5 +---- services/registration/root.vis | 10 +++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index 6d9bc03..45c5c9a 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,4 +1,4 @@ -CATCH incorrect_date_format 21 1 +CATCH incorrect_date_format incorrect_date_format_flag 1 LOAD save_yob 0 CATCH update_success allow_update_flag 1 MOUT back 0 @@ -7,6 +7,3 @@ INCMP _ 0 LOAD save_location 0 CATCH pin_entry single_edit_flag 1 INCMP enter_offerings * - - - diff --git a/services/registration/root.vis b/services/registration/root.vis index 14b77f8..3074d25 100644 --- a/services/registration/root.vis +++ b/services/registration/root.vis @@ -1,7 +1,7 @@ -CATCH select_language 8 0 -CATCH terms 9 0 +CATCH select_language language_set_flag 0 +CATCH terms account_created_flag 0 LOAD check_account_status 0 -CATCH account_pending 10 1 -CATCH create_pin 18 0 -CATCH main 11 1 +CATCH account_pending account_pending_flag 1 +CATCH create_pin pin_set_flag 0 +CATCH main account_success_flag 1 HALT From 143c9b14a1745bce6394fce7486ed5bed9a415cb Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 12:58:48 +0300 Subject: [PATCH 10/37] Renamed the flags --- services/registration/account_creation.vis | 2 +- services/registration/account_pending.vis | 2 +- services/registration/amount.vis | 2 +- services/registration/community_balance.vis | 4 +-- services/registration/create_pin.vis | 4 +-- services/registration/enter_location.vis | 6 ++-- services/registration/enter_offerings.vis | 4 +-- services/registration/enter_yob.vis | 4 +-- services/registration/my_balance.vis | 4 +-- services/registration/pp.csv | 32 ++++++++++----------- services/registration/root.vis | 10 +++---- services/registration/select_gender.vis | 4 +-- services/registration/send.vis | 2 +- services/registration/transaction_pin.vis | 2 +- services/registration/view_profile.vis | 4 +-- 15 files changed, 43 insertions(+), 43 deletions(-) diff --git a/services/registration/account_creation.vis b/services/registration/account_creation.vis index 1aca453..f4f326b 100644 --- a/services/registration/account_creation.vis +++ b/services/registration/account_creation.vis @@ -1,4 +1,4 @@ RELOAD verify_pin -CATCH create_pin_mismatch pin_mismatch_flag 1 +CATCH create_pin_mismatch flag_pin_mismatch 1 LOAD quit 0 HALT diff --git a/services/registration/account_pending.vis b/services/registration/account_pending.vis index a4baae5..d122613 100644 --- a/services/registration/account_pending.vis +++ b/services/registration/account_pending.vis @@ -1,3 +1,3 @@ RELOAD check_account_status -CATCH main account_success_flag 1 +CATCH main flag_account_success 1 HALT diff --git a/services/registration/amount.vis b/services/registration/amount.vis index e134735..9b2970a 100644 --- a/services/registration/amount.vis +++ b/services/registration/amount.vis @@ -5,7 +5,7 @@ MOUT back 0 HALT LOAD validate_amount 64 RELOAD validate_amount -CATCH invalid_amount invalid_amount_flag 1 +CATCH invalid_amount flag_invalid_amount 1 INCMP _ 0 LOAD get_recipient 12 LOAD get_sender 64 diff --git a/services/registration/community_balance.vis b/services/registration/community_balance.vis index c25ced3..151c6d8 100644 --- a/services/registration/community_balance.vis +++ b/services/registration/community_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin incorrect_pin_flag 1 -CATCH pin_entry account_authorized_flag 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/create_pin.vis b/services/registration/create_pin.vis index 2755b1d..e0e330f 100644 --- a/services/registration/create_pin.vis +++ b/services/registration/create_pin.vis @@ -1,9 +1,9 @@ LOAD create_account 0 -CATCH account_creation_failed account_creation_failed_flag 1 +CATCH account_creation_failed flag_account_creation_failed 1 MOUT exit 0 HALT LOAD save_pin 0 RELOAD save_pin -CATCH . incorrect_pin_flag 1 +CATCH . flag_incorrect_pin 1 INCMP quit 0 INCMP confirm_create_pin * diff --git a/services/registration/enter_location.vis b/services/registration/enter_location.vis index 45c5c9a..00bed3d 100644 --- a/services/registration/enter_location.vis +++ b/services/registration/enter_location.vis @@ -1,9 +1,9 @@ -CATCH incorrect_date_format incorrect_date_format_flag 1 +CATCH incorrect_date_format flag_incorrect_date_format 1 LOAD save_yob 0 -CATCH update_success allow_update_flag 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT INCMP _ 0 LOAD save_location 0 -CATCH pin_entry single_edit_flag 1 +CATCH pin_entry flag_single_edit 1 INCMP enter_offerings * diff --git a/services/registration/enter_offerings.vis b/services/registration/enter_offerings.vis index 9bc4407..e590321 100644 --- a/services/registration/enter_offerings.vis +++ b/services/registration/enter_offerings.vis @@ -1,6 +1,6 @@ LOAD save_location 0 -CATCH incorrect_pin incorrect_pin_flag 1 -CATCH update_success allow_update_flag 1 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT LOAD save_offerings 0 diff --git a/services/registration/enter_yob.vis b/services/registration/enter_yob.vis index 46a3771..3b27846 100644 --- a/services/registration/enter_yob.vis +++ b/services/registration/enter_yob.vis @@ -1,9 +1,9 @@ LOAD save_gender 0 -CATCH update_success allow_update_flag 1 +CATCH update_success flag_allow_update 1 MOUT back 0 HALT INCMP _ 0 LOAD verify_yob 8 LOAD save_yob 0 -CATCH pin_entry single_edit_flag 1 +CATCH pin_entry flag_single_edit 1 INCMP enter_location * diff --git a/services/registration/my_balance.vis b/services/registration/my_balance.vis index c25ced3..151c6d8 100644 --- a/services/registration/my_balance.vis +++ b/services/registration/my_balance.vis @@ -1,5 +1,5 @@ LOAD reset_incorrect 0 -CATCH incorrect_pin incorrect_pin_flag 1 -CATCH pin_entry account_authorized_flag 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 LOAD quit_with_balance 0 HALT diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 538870a..0501c1a 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -1,16 +1,16 @@ -flag,language_set_flag,8,checks whether the user has set their prefered language -flag,account_created_flag,9,this is set when an account has been created on the API -flag,account_pending_flag,10,this is set when an account does not have a status of SUCCESS -flag,account_success_flag,11,this is set when an account has a status of SUCCESS -flag,account_authorized_flag,12,this is set to allow a user access guarded nodes after providing a correct PIN -flag,invalid_recipient_flag,13,this is set when the transaction recipient is invalid -flag,invalid_recipient_with_invite_flag,14,this is set when the transaction recipient is valid but not on the platform -flag,incorrect_pin_flag,15,this is set when the provided PIN is invalid or does not match the current account's PIN -flag,allow_update_flag,16,this is set to allow a user to update their profile data -flag,invalid_amount_flag,17,this is set when the given transaction amount is invalid -flag,pin_set_flag,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu -flag,valid_pin_flag,19,this is set when the given PIN is valid -flag,pin_mismatch_flag,20,this is set when the confirmation PIN matches the initial PIN during registration -flag,incorrect_date_format_flag,21,this is set when the given year of birth is invalid -flag,account_creation_failed_flag,22,this is set when there's an error from the API during account creation -flag,single_edit_flag,23,this is set to allow a user to edit a single profile item such as familyName +flag,flag_language_set,8,checks whether the user has set their prefered language +flag,flag_account_created,9,this is set when an account has been created on the API +flag,flag_account_pending,10,this is set when an account does not have a status of SUCCESS +flag,flag_account_success,11,this is set when an account has a status of SUCCESS +flag,flag_account_authorized,12,this is set to allow a user access guarded nodes after providing a correct PIN +flag,flag_invalid_recipient,13,this is set when the transaction recipient is invalid +flag,flag_invalid_recipient_with_invite,14,this is set when the transaction recipient is valid but not on the platform +flag,flag_incorrect_pin,15,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,flag_allow_update,16,this is set to allow a user to update their profile data +flag,flag_invalid_amount,17,this is set when the given transaction amount is invalid +flag,flag_pin_set,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu +flag,flag_valid_pin,19,this is set when the given PIN is valid +flag,flag_pin_mismatch,20,this is set when the confirmation PIN matches the initial PIN during registration +flag,flag_incorrect_date_format,21,this is set when the given year of birth is invalid +flag,flag_account_creation_failed,22,this is set when there's an error from the API during account creation +flag,flag_single_edit,23,this is set to allow a user to edit a single profile item such as year of birth diff --git a/services/registration/root.vis b/services/registration/root.vis index 3074d25..6e3b79d 100644 --- a/services/registration/root.vis +++ b/services/registration/root.vis @@ -1,7 +1,7 @@ -CATCH select_language language_set_flag 0 -CATCH terms account_created_flag 0 +CATCH select_language flag_language_set 0 +CATCH terms flag_account_created 0 LOAD check_account_status 0 -CATCH account_pending account_pending_flag 1 -CATCH create_pin pin_set_flag 0 -CATCH main account_success_flag 1 +CATCH account_pending flag_account_pending 1 +CATCH create_pin flag_pin_set 0 +CATCH main flag_account_success 1 HALT diff --git a/services/registration/select_gender.vis b/services/registration/select_gender.vis index 1159c64..dd354fc 100644 --- a/services/registration/select_gender.vis +++ b/services/registration/select_gender.vis @@ -1,12 +1,12 @@ LOAD save_familyname 0 -CATCH update_success allow_update_flag 1 +CATCH update_success flag_allow_update 1 MOUT male 1 MOUT female 2 MOUT unspecified 3 MOUT back 0 HALT LOAD save_gender 0 -CATCH pin_entry single_edit_flag 1 +CATCH pin_entry flag_single_edit 1 INCMP _ 0 INCMP enter_yob 1 INCMP enter_yob 2 diff --git a/services/registration/send.vis b/services/registration/send.vis index 5db5287..e120302 100644 --- a/services/registration/send.vis +++ b/services/registration/send.vis @@ -3,6 +3,6 @@ MOUT back 0 HALT LOAD validate_recipient 20 RELOAD validate_recipient -CATCH invalid_recipient invalid_recipient_flag 1 +CATCH invalid_recipient flag_invalid_recipient 1 INCMP _ 0 INCMP amount * diff --git a/services/registration/transaction_pin.vis b/services/registration/transaction_pin.vis index 5fcf2e7..878104c 100644 --- a/services/registration/transaction_pin.vis +++ b/services/registration/transaction_pin.vis @@ -8,7 +8,7 @@ MOUT quit 9 HALT LOAD authorize_account 1 RELOAD authorize_account -CATCH incorrect_pin incorrect_pin_flag 1 +CATCH incorrect_pin flag_incorrect_pin 1 INCMP _ 0 INCMP quit 9 INCMP transaction_initiated * diff --git a/services/registration/view_profile.vis b/services/registration/view_profile.vis index bf1196b..2d0a508 100644 --- a/services/registration/view_profile.vis +++ b/services/registration/view_profile.vis @@ -1,8 +1,8 @@ LOAD get_profile_info 0 MAP get_profile_info LOAD reset_incorrect 0 -CATCH incorrect_pin incorrect_pin_flag 1 -CATCH pin_entry account_authorized_flag 0 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH pin_entry flag_account_authorized 0 MOUT back 0 HALT INCMP _ 0 From c3c5c836629c0006045581a92e85ef4ec063c6aa Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 14:48:02 +0300 Subject: [PATCH 11/37] Change the order of flags --- internal/models/flags.go | 12 ++++++------ services/registration/pp.csv | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/models/flags.go b/internal/models/flags.go index ba6d617..b64222c 100644 --- a/internal/models/flags.go +++ b/internal/models/flags.go @@ -5,18 +5,18 @@ import "git.defalsify.org/vise.git/state" const ( USERFLAG_LANGUAGE_SET = iota + state.FLAG_USERSTART USERFLAG_ACCOUNT_CREATED + USERFLAG_ACCOUNT_CREATION_FAILED USERFLAG_ACCOUNT_PENDING USERFLAG_ACCOUNT_SUCCESS + USERFLAG_PINMISMATCH + USERFLAG_PIN_SET USERFLAG_ACCOUNT_AUTHORIZED USERFLAG_INVALID_RECIPIENT USERFLAG_INVALID_RECIPIENT_WITH_INVITE - USERFLAG_INCORRECTPIN - USERFLAG_ALLOW_UPDATE USERFLAG_INVALID_AMOUNT - USERFLAG_PIN_SET + USERFLAG_INCORRECTPIN USERFLAG_VALIDPIN - USERFLAG_PINMISMATCH - USERFLAG_INCORRECTDATEFORMAT - USERFLAG_ACCOUNT_CREATION_FAILED + USERFLAG_ALLOW_UPDATE USERFLAG_SINGLE_EDIT + USERFLAG_INCORRECTDATEFORMAT ) diff --git a/services/registration/pp.csv b/services/registration/pp.csv index 0501c1a..fd552e4 100644 --- a/services/registration/pp.csv +++ b/services/registration/pp.csv @@ -1,16 +1,16 @@ flag,flag_language_set,8,checks whether the user has set their prefered language flag,flag_account_created,9,this is set when an account has been created on the API -flag,flag_account_pending,10,this is set when an account does not have a status of SUCCESS -flag,flag_account_success,11,this is set when an account has a status of SUCCESS -flag,flag_account_authorized,12,this is set to allow a user access guarded nodes after providing a correct PIN -flag,flag_invalid_recipient,13,this is set when the transaction recipient is invalid -flag,flag_invalid_recipient_with_invite,14,this is set when the transaction recipient is valid but not on the platform -flag,flag_incorrect_pin,15,this is set when the provided PIN is invalid or does not match the current account's PIN -flag,flag_allow_update,16,this is set to allow a user to update their profile data -flag,flag_invalid_amount,17,this is set when the given transaction amount is invalid -flag,flag_pin_set,18,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu -flag,flag_valid_pin,19,this is set when the given PIN is valid -flag,flag_pin_mismatch,20,this is set when the confirmation PIN matches the initial PIN during registration -flag,flag_incorrect_date_format,21,this is set when the given year of birth is invalid -flag,flag_account_creation_failed,22,this is set when there's an error from the API during account creation -flag,flag_single_edit,23,this is set to allow a user to edit a single profile item such as year of birth +flag,flag_account_creation_failed,10,this is set when there's an error from the API during account creation +flag,flag_account_pending,11,this is set when an account does not have a status of SUCCESS +flag,flag_account_success,12,this is set when an account has a status of SUCCESS +flag,flag_pin_mismatch,13,this is set when the confirmation PIN matches the initial PIN during registration +flag,flag_pin_set,14,this is set when a newly registered user sets a PIN. This must be present for an account to access the main menu +flag,flag_account_authorized,15,this is set to allow a user access guarded nodes after providing a correct PIN +flag,flag_invalid_recipient,16,this is set when the transaction recipient is invalid +flag,flag_invalid_recipient_with_invite,17,this is set when the transaction recipient is valid but not on the platform +flag,flag_invalid_amount,18,this is set when the given transaction amount is invalid +flag,flag_incorrect_pin,19,this is set when the provided PIN is invalid or does not match the current account's PIN +flag,flag_valid_pin,20,this is set when the given PIN is valid +flag,flag_allow_update,21,this is set to allow a user to update their profile data +flag,flag_single_edit,22,this is set to allow a user to edit a single profile item such as year of birth +flag,flag_incorrect_date_format,23,this is set when the given year of birth is invalid From 460e6fa2c8b46519393f3713ea4fe0aa85faad82 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 14:50:45 +0300 Subject: [PATCH 12/37] Renamed duplicate flag --- cmd/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 39136a9..26ad520 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -48,7 +48,7 @@ func main() { state.FlagDebugger.Register(models.USERFLAG_INVALID_AMOUNT, "INVALIDAMOUNT") state.FlagDebugger.Register(models.USERFLAG_ALLOW_UPDATE, "UNLOCKFORUPDATE") state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "VALIDPIN") - state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "ACCOUNTUNLOCKED") + state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_AUTHORIZED, "ACCOUNT_AUTHORIZED") state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATION_FAILED, "ACCOUNT_CREATION_FAILED") state.FlagDebugger.Register(models.USERFLAG_SINGLE_EDIT, "SINGLEEDIT") From 2559b4e6048d7007c6095f95dd5aa041811fa495 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 14:55:54 +0300 Subject: [PATCH 13/37] add test profile --- internal/handlers/ussd/menuhandler_test.go | 98 +++++++++++++++++++++- 1 file changed, 95 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 5755092..79c0c47 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -3,6 +3,7 @@ package ussd import ( "context" "encoding/json" + "fmt" "os" "path/filepath" "testing" @@ -690,7 +691,6 @@ func TestSaveOfferings(t *testing.T) { } } - func TestSaveGender(t *testing.T) { // Create a new instance of MockAccountFileHandler mockFileHandler := new(mocks.MockAccountFileHandler) @@ -732,7 +732,7 @@ func TestSaveGender(t *testing.T) { expectedError: nil, expectedGender: "Unspecified", }, - + { name: "Empty Input", input: []byte{}, @@ -752,7 +752,7 @@ func TestSaveGender(t *testing.T) { mockFileHandler.On("WriteAccountData", mock.MatchedBy(func(data map[string]string) bool { return data["Gender"] == tt.expectedGender })).Return(tt.writeError) - } else if len(tt.input) == 0 { + } else if len(tt.input) == 0 { // For empty input, no WriteAccountData call should be made mockFileHandler.On("WriteAccountData", mock.Anything).Maybe().Return(tt.writeError) } @@ -876,3 +876,95 @@ func TestGetAmount(t *testing.T) { } } +func TestGetProfileInfo(t *testing.T) { + tests := []struct { + name string + accountData map[string]string + readError error + expectedResult resource.Result + expectedError error + }{ + { + name: "Complete Profile", + accountData: map[string]string{ + "FirstName": "John", + "FamilyName": "Doe", + "Gender": "Male", + "YOB": "1980", + "Location": "Mombasa", + "Offerings": "Product A", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n", + "John", "Doe", "Male", 44, "Mombasa", "Product A", + ), + }, + expectedError: nil, + }, + { + name: "Profile with Not Provided Fields", + accountData: map[string]string{ + "FirstName": "Not provided", + "FamilyName": "Doe", + "Gender": "Female", + "YOB": "1995", + "Location": "Not provided", + "Offerings": "Service B", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %d\nLocation: %s\nYou provide: %s\n", + "Not provided", "Female", 29, "Not provided", "Service B", + ), + }, + expectedError: nil, + }, + { + name: "Profile with YOB as Not provided", + accountData: map[string]string{ + "FirstName": "Not provided", + "FamilyName": "Doe", + "Gender": "Female", + "YOB": "Not provided", + "Location": "Not provided", + "Offerings": "Service B", + }, + readError: nil, + expectedResult: resource.Result{ + Content: fmt.Sprintf( + "Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", + "Not provided", "Female", "Not provided", "Not provided", "Service B", + ), + }, + expectedError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a new instance of MockAccountFileHandler + mockFileHandler := new(mocks.MockAccountFileHandler) + + // Set up the mock expectations + mockFileHandler.On("ReadAccountData").Return(tt.accountData, tt.readError) + + // Create the Handlers instance with the mock file handler + h := &Handlers{ + accountFileHandler: mockFileHandler, + } + + // Call the method + result, err := h.GetProfileInfo(context.Background(), "get_profile_info", nil) + + // Assert the results + assert.Equal(t, tt.expectedResult, result) + assert.Equal(t, tt.expectedError, err) + + // Assert all expectations were met + mockFileHandler.AssertExpectations(t) + }) + } +} \ No newline at end of file From 07c8638a6d312463ce0f87ff79b52c9dda3c8ea0 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Sat, 31 Aug 2024 15:09:34 +0300 Subject: [PATCH 14/37] use correct symbol name --- services/registration/balances.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/balances.vis b/services/registration/balances.vis index 552acdb..dd14501 100644 --- a/services/registration/balances.vis +++ b/services/registration/balances.vis @@ -1,4 +1,4 @@ -LOAD reset_unlocked 0 +LOAD reset_account_authorized 0 MOUT my_balance 1 MOUT community_balance 2 MOUT back 0 From 632dd860ffdfde2c61bcf99b95eef1a46dc50f58 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Sat, 31 Aug 2024 17:16:57 +0300 Subject: [PATCH 15/37] Use flags from pp.csv on the Authorize function --- internal/handlers/ussd/menuhandler.go | 41 +++++++++++++++++++++------ 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 91f4a4e..a0ce8b5 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -64,6 +64,18 @@ func isValidPIN(pin string) bool { return match } +func (h *Handlers) PreloadFlags(flagKeys []string) (map[string]uint32, error) { + flags := make(map[string]uint32) + for _, key := range flagKeys { + flag, err := h.parser.GetFlag(key) + if err != nil { + return nil, err + } + flags[key] = flag + } + return flags, nil +} + // SetLanguage sets the language across the menu func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { inputStr := string(input) @@ -385,19 +397,25 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, err } + // Preload the required flags + flagKeys := []string{"flag_incorrect_pin", "flag_account_authorized", "flag_allow_update"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + if len(input) == 4 { if pin != accountData["AccountPIN"] { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTPIN) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } - if h.fs.St.MatchFlag(models.USERFLAG_ACCOUNT_AUTHORIZED, false) { - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) - res.FlagSet = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_AUTHORIZED) + if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) + res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) } else { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) } } return res, nil @@ -780,6 +798,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, err } - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + account_authorized_flag, err := h.parser.GetFlag("flag_account_authorized") + + if err != nil { + res.FlagReset = append(res.FlagReset, account_authorized_flag) + } + return res, nil } From ba4d0abcdadfc378879e7a2e0270640c367f4e6b Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 10:03:57 +0300 Subject: [PATCH 16/37] improve doc line --- 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 a0ce8b5..8c85414 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -496,7 +496,7 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res return res, nil } -// ResetIncorrectYob resets the incorrect date format after a new attempt +// ResetIncorrectYob resets the incorrect date format flag after a new attempt func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTDATEFORMAT) From 31be1fa22107ba12443eda7d1395a78aad71a9ce Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 2 Sep 2024 14:00:02 +0300 Subject: [PATCH 17/37] Use flags from pp.csv on the menuhandler functions and tests --- internal/handlers/ussd/menuhandler.go | 258 ++++++++++++++++----- internal/handlers/ussd/menuhandler_test.go | 55 +++-- 2 files changed, 236 insertions(+), 77 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a0ce8b5..843b900 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -15,7 +15,6 @@ import ( "git.defalsify.org/vise.git/resource" "git.defalsify.org/vise.git/state" "git.grassecon.net/urdt/ussd/internal/handlers/server" - "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" "gopkg.in/leonelquinteros/gotext.v1" ) @@ -30,29 +29,33 @@ type FSData struct { St *state.State } +type FlagParserInterface interface { + GetFlag(key string) (uint32, error) +} + type Handlers struct { fs *FSData - parser *asm.FlagParser + parser FlagParserInterface accountFileHandler utils.AccountFileHandlerInterface accountService server.AccountServiceInterface } func NewHandlers(dir string, st *state.State) (*Handlers, error) { - pfp := path.Join(scriptDir, "pp.csv") - parser := asm.NewFlagParser() - _, err := parser.Load(pfp) - if err != nil { - return nil, err - } - return &Handlers{ - fs: &FSData{ - Path: dir, - St: st, - }, - parser: parser, - accountFileHandler: utils.NewAccountFileHandler(dir + "_data"), - accountService: &server.AccountService{}, - }, nil + pfp := path.Join(scriptDir, "pp.csv") + parser := asm.NewFlagParser() + _, err := parser.Load(pfp) + if err != nil { + return nil, err + } + return &Handlers{ + fs: &FSData{ + Path: dir, + St: st, + }, + parser: parser, + accountFileHandler: utils.NewAccountFileHandler(dir + "_data"), + accountService: &server.AccountService{}, + }, nil } // Define the regex pattern as a constant @@ -78,8 +81,16 @@ func (h *Handlers) PreloadFlags(flagKeys []string) (map[string]uint32, error) { // SetLanguage sets the language across the menu func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { - inputStr := string(input) res := resource.Result{} + + // Preload the required flag + flagKeys := []string{"flag_language_set"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + inputStr := string(input) switch inputStr { case "0": res.FlagSet = []uint32{state.FLAG_LANG} @@ -90,7 +101,7 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r default: } - res.FlagSet = append(res.FlagSet, models.USERFLAG_LANGUAGE_SET) + res.FlagSet = append(res.FlagSet, flags["flag_language_set"]) return res, nil } @@ -101,7 +112,14 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - err := h.accountFileHandler.EnsureFileExists() + // Preload the required flags + flagKeys := []string{"flag_account_created", "flag_account_creation_failed"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + err = h.accountFileHandler.EnsureFileExists() if err != nil { return res, err } @@ -114,7 +132,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) accountResp, err := h.accountService.CreateAccount() if err != nil { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATION_FAILED) + res.FlagSet = append(res.FlagSet, flags["flag_account_creation_failed"]) return res, err } @@ -135,13 +153,21 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, err } - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATED) + res.FlagSet = append(res.FlagSet, flags["flag_account_created"]) return res, err } // SavePin persists the user's PIN choice into the filesystem func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flags + flagKeys := []string{"flag_incorrect_pin"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + accountPIN := string(input) accountData, err := h.accountFileHandler.ReadAccountData() @@ -151,11 +177,11 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTPIN) + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) return res, nil } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) accountData["AccountPIN"] = accountPIN err = h.accountFileHandler.WriteAccountData(accountData) @@ -170,18 +196,26 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} menuOption := string(input) + + // Preload the required flags + flagKeys := []string{"flag_allow_update", "flag_single_edit"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + switch menuOption { case "2": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "3": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "4": - res.FlagReset = append(res.FlagSet, models.USERFLAG_ALLOW_UPDATE) - res.FlagSet = append(res.FlagSet, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) default: - res.FlagReset = append(res.FlagReset, models.USERFLAG_SINGLE_EDIT) + res.FlagReset = append(res.FlagReset, flags["flag_single_edit"]) } return res, nil @@ -193,17 +227,24 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + // Preload the required flags + flagKeys := []string{"flag_valid_pin", "flag_pin_mismatch", "flag_pin_set"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err } if bytes.Equal(input, []byte(accountData["AccountPIN"])) { - res.FlagSet = []uint32{models.USERFLAG_VALIDPIN} - res.FlagReset = []uint32{models.USERFLAG_PINMISMATCH} - res.FlagSet = append(res.FlagSet, models.USERFLAG_PIN_SET) + res.FlagSet = []uint32{flags["flag_valid_pin"]} + res.FlagReset = []uint32{flags["flag_pin_mismatch"]} + res.FlagSet = append(res.FlagSet, flags["flag_pin_set"]) } else { - res.FlagSet = []uint32{models.USERFLAG_PINMISMATCH} + res.FlagSet = []uint32{flags["flag_pin_mismatch"]} } return res, nil @@ -361,14 +402,30 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) // ResetAllowUpdate resets the allowupdate flag that allows a user to update profile data. func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_ALLOW_UPDATE) + + // Preload the required flag + flagKeys := []string{"flag_allow_update"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) return res, nil } // ResetAccountAuthorized resets the account authorization flag after a successful PIN entry. func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + + // Preload the required flags + flagKeys := []string{"flag_account_authorized"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } @@ -390,12 +447,6 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte // It sets the required flags that control the flow. func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - pin := string(input) - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } // Preload the required flags flagKeys := []string{"flag_incorrect_pin", "flag_account_authorized", "flag_allow_update"} @@ -404,6 +455,13 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, err } + pin := string(input) + + accountData, err := h.accountFileHandler.ReadAccountData() + if err != nil { + return res, err + } + if len(input) == 4 { if pin != accountData["AccountPIN"] { res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) @@ -424,7 +482,15 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res // ResetIncorrectPin resets the incorrect pin flag after a new PIN attempt. func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) + + // Preload the required flag + flagKeys := []string{"flag_incorrect_pin"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) return res, nil } @@ -433,6 +499,13 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + // Preload the required flags + flagKeys := []string{"flag_account_success", "flag_account_pending"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err @@ -448,11 +521,11 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b accountData["Status"] = status if status == "SUCCESS" { - res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_SUCCESS) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_PENDING) + res.FlagSet = append(res.FlagSet, flags["flag_account_success"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_pending"]) } else { - res.FlagReset = append(res.FlagSet, models.USERFLAG_ACCOUNT_SUCCESS) - res.FlagSet = append(res.FlagReset, models.USERFLAG_ACCOUNT_PENDING) + res.FlagReset = append(res.FlagSet, flags["flag_account_success"]) + res.FlagSet = append(res.FlagReset, flags["flag_account_pending"]) } err = h.accountFileHandler.WriteAccountData(accountData) @@ -467,30 +540,45 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + // Preload the required flags + flagKeys := []string{"flag_account_authorized"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") res.Content = l.Get("Thank you for using Sarafu. Goodbye!") - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } // VerifyYob verifies the length of the given input func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flag + flagKeys := []string{"flag_incorrect_date_format"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + date := string(input) - _, err := strconv.Atoi(date) + _, err = strconv.Atoi(date) if err != nil { // If conversion fails, input is not numeric - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_date_format"]) return res, nil } if len(date) == 4 { - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_date_format"]) } else { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INCORRECTDATEFORMAT) + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_date_format"]) } return res, nil @@ -499,7 +587,15 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res // ResetIncorrectYob resets the incorrect date format after a new attempt func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTDATEFORMAT) + + // Preload the required flags + flagKeys := []string{"flag_incorrect_date_format"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_date_format"]) return res, nil } @@ -525,6 +621,14 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( // 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) { res := resource.Result{} + + // Preload the required flags + flagKeys := []string{"flag_invalid_recipient"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + recipient := string(input) accountData, err := h.accountFileHandler.ReadAccountData() @@ -535,7 +639,7 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by if recipient != "0" { // mimic invalid number check if recipient == "000" { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_RECIPIENT) + res.FlagSet = append(res.FlagSet, flags["flag_invalid_recipient"]) res.Content = recipient return res, nil @@ -556,6 +660,14 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by // as well as the invalid flags func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flags + flagKeys := []string{"flag_invalid_recipient", "flag_invalid_recipient_with_invite"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err @@ -570,7 +682,7 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt return res, err } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_RECIPIENT, models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE) + res.FlagReset = append(res.FlagReset, flags["flag_invalid_recipient"], flags["flag_invalid_recipient_with_invite"]) return res, nil } @@ -578,6 +690,14 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt // ResetTransactionAmount resets the transaction amount and invalid flag func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flag + flagKeys := []string{"flag_invalid_amount"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err @@ -591,7 +711,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input return res, err } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_AMOUNT) + res.FlagReset = append(res.FlagReset, flags["flag_invalid_amount"]) return res, nil } @@ -620,6 +740,14 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flag + flagKeys := []string{"flag_invalid_amount"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + amountStr := string(input) accountData, err := h.accountFileHandler.ReadAccountData() @@ -647,20 +775,20 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) re := regexp.MustCompile(`^(\d+(\.\d+)?)\s*(?:CELO)?$`) matches := re.FindStringSubmatch(strings.TrimSpace(amountStr)) if len(matches) < 2 { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) res.Content = amountStr return res, nil } inputAmount, err := strconv.ParseFloat(matches[1], 64) if err != nil { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) res.Content = amountStr return res, nil } if inputAmount > balanceValue { - res.FlagSet = append(res.FlagSet, models.USERFLAG_INVALID_AMOUNT) + res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) res.Content = amountStr return res, nil } @@ -755,6 +883,14 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res // gracefully exiting the session. func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + + // Preload the required flag + flagKeys := []string{"flag_account_authorized"} + flags, err := h.PreloadFlags(flagKeys) + if err != nil { + return res, err + } + code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") @@ -767,7 +903,7 @@ func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte return res, nil } res.Content = l.Get("Your account balance is %s", balance) - res.FlagReset = append(res.FlagReset, models.USERFLAG_ACCOUNT_AUTHORIZED) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } diff --git a/internal/handlers/ussd/menuhandler_test.go b/internal/handlers/ussd/menuhandler_test.go index 79c0c47..5bcf7c1 100644 --- a/internal/handlers/ussd/menuhandler_test.go +++ b/internal/handlers/ussd/menuhandler_test.go @@ -21,6 +21,15 @@ type MockAccountService struct { mock.Mock } +type MockFlagParser struct { + mock.Mock +} + +func (m *MockFlagParser) GetFlag(key string) (uint32, error) { + args := m.Called(key) + return args.Get(0).(uint32), args.Error(1) +} + func (m *MockAccountService) CreateAccount() (*models.AccountResponse, error) { args := m.Called() return args.Get(0).(*models.AccountResponse), args.Error(1) @@ -70,11 +79,20 @@ func TestCreateAccount(t *testing.T) { // Set up expectations for the mock account service mockAccountService.On("CreateAccount").Return(mockAccountResponse, nil) + mockParser := new(MockFlagParser) + + flag_account_created := uint32(1) + flag_account_creation_failed := uint32(2) + + mockParser.On("GetFlag", "flag_account_created").Return(flag_account_created, nil) + mockParser.On("GetFlag", "flag_account_creation_failed").Return(flag_account_creation_failed, nil) + // Initialize Handlers with mock account service h := &Handlers{ fs: &FSData{Path: accountFilePath}, accountFileHandler: accountFileHandler, accountService: mockAccountService, + parser: mockParser, } tests := []struct { @@ -87,7 +105,7 @@ func TestCreateAccount(t *testing.T) { name: "New account creation", existingData: nil, expectedResult: resource.Result{ - FlagSet: []uint32{models.USERFLAG_ACCOUNT_CREATED}, + FlagSet: []uint32{flag_account_created}, }, expectedData: map[string]string{ "TrackingId": "test-tracking-id", @@ -248,10 +266,16 @@ func TestSavePin(t *testing.T) { // Create a new AccountFileHandler and set it in the Handlers struct accountFileHandler := utils.NewAccountFileHandler(accountFilePath) + mockParser := new(MockFlagParser) + h := &Handlers{ accountFileHandler: accountFileHandler, + parser: mockParser, } + flag_incorrect_pin := uint32(1) + mockParser.On("GetFlag", "flag_incorrect_pin").Return(flag_incorrect_pin, nil) + tests := []struct { name string input []byte @@ -272,21 +296,21 @@ func TestSavePin(t *testing.T) { { name: "Invalid PIN - non-numeric", input: []byte("12ab"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, { name: "Invalid PIN - less than 4 digits", input: []byte("123"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, { name: "Invalid PIN - more than 4 digits", input: []byte("12345"), - expectedFlags: []uint32{models.USERFLAG_INCORRECTPIN}, + expectedFlags: []uint32{flag_incorrect_pin}, expectedData: initialAccountData, // No changes expected expectedErrors: false, }, @@ -294,7 +318,6 @@ func TestSavePin(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Ensure the file exists before running the test err := accountFileHandler.EnsureFileExists() if err != nil { t.Fatalf("Failed to ensure account file exists: %v", err) @@ -906,12 +929,12 @@ func TestGetProfileInfo(t *testing.T) { { name: "Profile with Not Provided Fields", accountData: map[string]string{ - "FirstName": "Not provided", + "FirstName": "Not provided", "FamilyName": "Doe", - "Gender": "Female", - "YOB": "1995", - "Location": "Not provided", - "Offerings": "Service B", + "Gender": "Female", + "YOB": "1995", + "Location": "Not provided", + "Offerings": "Service B", }, readError: nil, expectedResult: resource.Result{ @@ -925,12 +948,12 @@ func TestGetProfileInfo(t *testing.T) { { name: "Profile with YOB as Not provided", accountData: map[string]string{ - "FirstName": "Not provided", + "FirstName": "Not provided", "FamilyName": "Doe", - "Gender": "Female", - "YOB": "Not provided", - "Location": "Not provided", - "Offerings": "Service B", + "Gender": "Female", + "YOB": "Not provided", + "Location": "Not provided", + "Offerings": "Service B", }, readError: nil, expectedResult: resource.Result{ @@ -967,4 +990,4 @@ func TestGetProfileInfo(t *testing.T) { mockFileHandler.AssertExpectations(t) }) } -} \ No newline at end of file +} From fe4df78f800ccf1d745ae15cd070acc5f8b1aaad Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 16:04:53 +0300 Subject: [PATCH 18/37] use gdbm for user info storage --- internal/handlers/ussd/menuhandler.go | 421 ++++++++++++++------------ 1 file changed, 232 insertions(+), 189 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 8c85414..6e8501d 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -3,6 +3,7 @@ package ussd import ( "bytes" "context" + "errors" "fmt" "path" "regexp" @@ -17,14 +18,36 @@ import ( "git.grassecon.net/urdt/ussd/internal/handlers/server" "git.grassecon.net/urdt/ussd/internal/models" "git.grassecon.net/urdt/ussd/internal/utils" + "github.com/graygnuorg/go-gdbm" "gopkg.in/leonelquinteros/gotext.v1" ) var ( scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") + dbFile = path.Join(scriptDir, "vise.gdbm") ) +const ( + TrackingIdKey = "TRACKINGID" + PublicKeyKey = "PUBLICKEY" + CustodialIdKey = "CUSTODIALID" + AccountPin = "ACCOUNTPIN" + AccountStatus = "ACCOUNTSTATUS" + FirstName = "FIRSTNAME" + FamilyName = "FAMILYNAME" + YearOfBirth = "YOB" + Location = "LOCATION" + Gender = "GENDER" + Offerings = "OFFERINGS" + Recipient = "RECIPIENT" + Amount = "AMOUNT" +) + +func toBytes(s string) []byte { + return []byte(s) +} + type FSData struct { Path string St *state.State @@ -32,19 +55,25 @@ type FSData struct { type Handlers struct { fs *FSData + db *gdbm.Database parser *asm.FlagParser accountFileHandler utils.AccountFileHandlerInterface accountService server.AccountServiceInterface } func NewHandlers(dir string, st *state.State) (*Handlers, error) { + db, err := gdbm.Open(dbFile, gdbm.ModeWrcreat) + if err != nil { + panic(err) + } pfp := path.Join(scriptDir, "pp.csv") parser := asm.NewFlagParser() - _, err := parser.Load(pfp) + _, err = parser.Load(pfp) if err != nil { return nil, err } return &Handlers{ + db: db, fs: &FSData{ Path: dir, St: st, @@ -117,22 +146,18 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATION_FAILED) return res, err } - - accountData := map[string]string{ - "TrackingId": accountResp.Result.TrackingId, - "PublicKey": accountResp.Result.PublicKey, - "CustodialId": accountResp.Result.CustodialId.String(), - "Status": "PENDING", - "Gender": "Not provided", - "YOB": "Not provided", - "Location": "Not provided", - "Offerings": "Not provided", - "FirstName": "Not provided", - "FamilyName": "Not provided", + data := map[string]string{ + TrackingIdKey: accountResp.Result.TrackingId, + PublicKeyKey: accountResp.Result.PublicKey, + CustodialIdKey: accountResp.Result.CustodialId.String(), } - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err + + for key, value := range data { + err := h.db.Store(toBytes(key), toBytes(value), true) + if err != nil { + return res, err + + } } res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_CREATED) @@ -144,10 +169,10 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou res := resource.Result{} accountPIN := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { @@ -156,12 +181,17 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou } res.FlagReset = append(res.FlagReset, models.USERFLAG_INCORRECTPIN) - accountData["AccountPIN"] = accountPIN + //accountData["AccountPIN"] = accountPIN - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(AccountPin) + value := []byte(accountPIN) + + h.db.Store(key, value, true) + + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } return res, nil } @@ -192,20 +222,20 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b // to access the main menu func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { + pin, err := h.db.Fetch([]byte(AccountPin)) + if err == nil { + if bytes.Equal(input, pin) { + res.FlagSet = []uint32{models.USERFLAG_VALIDPIN} + res.FlagReset = []uint32{models.USERFLAG_PINMISMATCH} + res.FlagSet = append(res.FlagSet, models.USERFLAG_PIN_SET) + } else { + res.FlagSet = []uint32{models.USERFLAG_PINMISMATCH} + } + } else if errors.Is(err, gdbm.ErrItemNotFound) { + //PIN not set yet + } else { return res, err } - - if bytes.Equal(input, []byte(accountData["AccountPIN"])) { - res.FlagSet = []uint32{models.USERFLAG_VALIDPIN} - res.FlagReset = []uint32{models.USERFLAG_PINMISMATCH} - res.FlagSet = append(res.FlagSet, models.USERFLAG_PIN_SET) - } else { - res.FlagSet = []uint32{models.USERFLAG_PINMISMATCH} - } - return res, nil } @@ -224,18 +254,23 @@ func codeFromCtx(ctx context.Context) string { func (h *Handlers) SaveFirstname(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } if len(input) > 0 { name := string(input) - accountData["FirstName"] = name + //accountData["FirstName"] = name - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + key := []byte(FirstName) + value := []byte(name) + + h.db.Store(key, value, true) + + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } } return res, nil @@ -244,19 +279,12 @@ func (h *Handlers) SaveFirstname(cxt context.Context, sym string, input []byte) // SaveFamilyname updates the family name in a JSON data file with the provided input. func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } if len(input) > 0 { secondname := string(input) - accountData["FamilyName"] = secondname + key := []byte(FamilyName) + value := []byte(secondname) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) } return res, nil @@ -266,20 +294,18 @@ func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - yob := string(input) if len(yob) == 4 { yob := string(input) - accountData["YOB"] = yob + //accountData["YOB"] = yob + key := []byte(YearOfBirth) + value := []byte(yob) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } } return res, nil @@ -289,19 +315,17 @@ func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resou func (h *Handlers) SaveLocation(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } if len(input) > 0 { location := string(input) - accountData["Location"] = location + key := []byte(Location) + value := []byte(location) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) } return res, nil @@ -310,12 +334,6 @@ func (h *Handlers) SaveLocation(cxt context.Context, sym string, input []byte) ( // SaveGender updates the gender in a JSON data file with the provided input. func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - if len(input) > 0 { gender := string(input) @@ -327,12 +345,16 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re case "3": gender = "Unspecified" } - accountData["Gender"] = gender + //accountData["Gender"] = gender + key := []byte(Gender) + value := []byte(gender) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) + + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } } return res, nil } @@ -341,19 +363,23 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } if len(input) > 0 { offerings := string(input) - accountData["Offerings"] = offerings + //accountData["Offerings"] = offerings + key := []byte(Offerings) + value := []byte(offerings) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) + + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } } return res, nil } @@ -376,12 +402,16 @@ func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - res.Content = accountData["PublicKey"] + res.Content = string(publicKey) return res, nil } @@ -390,12 +420,12 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte // It sets the required flags that control the flow. func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - pin := string(input) + //pin := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } // Preload the required flags flagKeys := []string{"flag_incorrect_pin", "flag_account_authorized", "flag_allow_update"} @@ -404,20 +434,43 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, err } - if len(input) == 4 { - if pin != accountData["AccountPIN"] { - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) - return res, nil - } - if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) - res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) - } else { - res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + storedpin, err := h.db.Fetch([]byte(AccountPin)) + if err == nil { + if len(input) == 4 { + if bytes.Equal(input, storedpin) { + if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { + res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) + res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) + } else { + res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + } + } else { + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + return res, nil + } } + } else if errors.Is(err, gdbm.ErrItemNotFound) { + //PIN not set yet + } else { + return res, err } + + // if len(input) == 4 { + // if pin != accountData["AccountPIN"] { + // res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) + // res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + // return res, nil + // } + // if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { + // res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) + // res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) + // } else { + // res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) + // res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + // } + // } return res, nil } @@ -433,19 +486,29 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } + trackingId, err := h.db.Fetch([]byte(TrackingIdKey)) + if err != nil { return res, err } - status, err := h.accountService.CheckAccountStatus(accountData["TrackingId"]) + status, err := h.accountService.CheckAccountStatus(string(trackingId)) if err != nil { fmt.Println("Error checking account status:", err) - return res, nil + return res, err } - accountData["Status"] = status + //accountData["Status"] = status + err = h.db.Store(toBytes(TrackingIdKey), toBytes(status), true) + + if err != nil { + return res, nil + } if status == "SUCCESS" { res.FlagSet = append(res.FlagSet, models.USERFLAG_ACCOUNT_SUCCESS) @@ -455,10 +518,10 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b res.FlagSet = append(res.FlagReset, models.USERFLAG_ACCOUNT_PENDING) } - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + // err = h.accountFileHandler.WriteAccountData(accountData) + // if err != nil { + // return res, err + // } return res, nil } @@ -507,13 +570,13 @@ func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []by // the balance as the result content func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) - accountData, err := h.accountFileHandler.ReadAccountData() if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } @@ -526,12 +589,6 @@ func (h *Handlers) CheckBalance(ctx context.Context, sym string, input []byte) ( func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} recipient := string(input) - - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - if recipient != "0" { // mimic invalid number check if recipient == "000" { @@ -541,12 +598,11 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by return res, nil } - accountData["Recipient"] = recipient + // accountData["Recipient"] = recipient + key := []byte(Recipient) + value := []byte(recipient) - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } + h.db.Store(key, value, true) } return res, nil @@ -556,20 +612,14 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by // as well as the invalid flags func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err + err := h.db.Delete([]byte(Amount)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { + panic(err) } - - // reset the transaction - accountData["Recipient"] = "" - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err + err = h.db.Delete([]byte(Recipient)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { + panic(err) } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_RECIPIENT, models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE) return res, nil @@ -578,21 +628,11 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt // ResetTransactionAmount resets the transaction amount and invalid flag func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err + err := h.db.Delete([]byte(Amount)) + if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { + panic(err) } - - // reset the amount - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) - if err != nil { - return res, err - } - res.FlagReset = append(res.FlagReset, models.USERFLAG_INVALID_AMOUNT) - return res, nil } @@ -600,13 +640,12 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input // the result content. func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } @@ -621,13 +660,12 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} amountStr := string(input) - - accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - balanceStr, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balanceStr, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, err } @@ -666,9 +704,10 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) } res.Content = fmt.Sprintf("%.3f", inputAmount) // Format to 3 decimal places - accountData["Amount"] = res.Content + key := []byte(Amount) + value := []byte(res.Content) + h.db.Store(key, value, true) - err = h.accountFileHandler.WriteAccountData(accountData) if err != nil { return res, err } @@ -679,13 +718,12 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) // GetRecipient returns the transaction recipient from a JSON data file. func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - accountData, err := h.accountFileHandler.ReadAccountData() + recipient, err := h.db.Fetch([]byte(Recipient)) if err != nil { return res, err } - res.Content = accountData["Recipient"] + res.Content = string(recipient) return res, nil } @@ -727,12 +765,13 @@ func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + //accountData, err := h.accountFileHandler.ReadAccountData() + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - res.Content = accountData["PublicKey"] + res.Content = string(publicKey) return res, nil } @@ -741,12 +780,13 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - accountData, err := h.accountFileHandler.ReadAccountData() + //accountData, err := h.accountFileHandler.ReadAccountData() + amount, err := h.db.Fetch([]byte(Amount)) if err != nil { return res, err } - res.Content = accountData["Amount"] + res.Content = string(amount) return res, nil } @@ -758,11 +798,15 @@ func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - accountData, err := h.accountFileHandler.ReadAccountData() + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - balance, err := h.accountService.CheckBalance(accountData["PublicKey"]) + balance, err := h.accountService.CheckBalance(string(publicKey)) if err != nil { return res, nil } @@ -779,24 +823,23 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - // TODO // Use the amount, recipient and sender to call the API and initialize the transaction - res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", accountData["Recipient"], accountData["Amount"], accountData["PublicKey"]) - - // reset the transaction - accountData["Recipient"] = "" - accountData["Amount"] = "" - - err = h.accountFileHandler.WriteAccountData(accountData) + publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } + amount, err := h.db.Fetch([]byte(Amount)) + if err != nil { + return res, err + } + recipient, err := h.db.Fetch([]byte(Recipient)) + if err != nil { + return res, err + } + + res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) account_authorized_flag, err := h.parser.GetFlag("flag_account_authorized") From 0c96831378d12237cd26a4e6a055999f7767d6c5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 16:05:13 +0300 Subject: [PATCH 19/37] use gdbm --- cmd/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 26ad520..68b468a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -83,9 +83,9 @@ func main() { fp := path.Join(dp, sessionId) - ussdHandlers,err := ussd.NewHandlers(fp, &st) + ussdHandlers, err := ussd.NewHandlers(fp, &st) - if(err != nil){ + if err != nil { fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err) } From 2b511aba4cd74b3443da3ea26b8020c66d098f21 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 16:39:04 +0300 Subject: [PATCH 20/37] remove commented code --- internal/handlers/ussd/menuhandler.go | 54 ++++++++------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index a956d6b..b7789b0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -483,12 +483,12 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res return res, err } - pin := string(input) + // pin := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } + // accountData, err := h.accountFileHandler.ReadAccountData() + // if err != nil { + // return res, err + // } storedpin, err := h.db.Fetch([]byte(AccountPin)) if err == nil { @@ -512,21 +512,6 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res } else { return res, err } - - // if len(input) == 4 { - // if pin != accountData["AccountPIN"] { - // res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) - // res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) - // return res, nil - // } - // if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { - // res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) - // res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) - // } else { - // res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) - // res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) - // } - // } return res, nil } @@ -550,14 +535,6 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - - flagKeys := []string{"flag_account_success", "flag_account_pending"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } - // Preload the required flags flagKeys := []string{"flag_account_success", "flag_account_pending"} flags, err := h.PreloadFlags(flagKeys) @@ -693,13 +670,12 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by res := resource.Result{} recipient := string(input) - accountData, err := h.accountFileHandler.ReadAccountData() + flagKeys := []string{"flag_invalid_recipient"} + flags, err := h.PreloadFlags(flagKeys) if err != nil { return res, err } - - recipient := string(input) if recipient != "0" { // mimic invalid number check if recipient == "000" { @@ -731,7 +707,7 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt return res, err } - err := h.db.Delete([]byte(Amount)) + err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { panic(err) } @@ -756,7 +732,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input return res, err } - err := h.db.Delete([]byte(Amount)) + err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { panic(err) } @@ -789,7 +765,7 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} -// Preload the required flag + // Preload the required flag flagKeys := []string{"flag_invalid_amount"} flags, err := h.PreloadFlags(flagKeys) if err != nil { @@ -970,11 +946,11 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] l := gotext.NewLocale(translationDir, code) l.AddDomain("default") // Preload the required flags - flagKeys := []string{"flag_invalid_recipient"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + // flagKeys := []string{"flag_invalid_recipient"} + // flags, err := h.PreloadFlags(flagKeys) + // if err != nil { + // return res, err + // } // TODO // Use the amount, recipient and sender to call the API and initialize the transaction From b6e4ba7ede0ae0aaf0856ba6dbe8c7e95d5b0f74 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 2 Sep 2024 17:08:57 +0300 Subject: [PATCH 21/37] Verify PIN using the one in the db --- 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 b7789b0..be9fe73 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -262,12 +262,11 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res return res, err } - accountData, err := h.accountFileHandler.ReadAccountData() + AccountPin, err := h.db.Fetch([]byte(AccountPin)) if err != nil { return res, err } - - if bytes.Equal(input, []byte(accountData["AccountPIN"])) { + if bytes.Equal(input, AccountPin) { res.FlagSet = []uint32{flags["flag_valid_pin"]} res.FlagReset = []uint32{flags["flag_pin_mismatch"]} res.FlagSet = append(res.FlagSet, flags["flag_pin_set"]) From 14c8230fd4235b61508574acdaa0a58ca44e3ff5 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 17:11:08 +0300 Subject: [PATCH 22/37] code cleanup --- internal/handlers/ussd/menuhandler.go | 35 ++++++++------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index b7789b0..a8c48db 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -148,16 +148,16 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) return res, err } - err = h.accountFileHandler.EnsureFileExists() - if err != nil { - return res, err - } + // err = h.accountFileHandler.EnsureFileExists() + // if err != nil { + // return res, err + // } // if an account exists, return to prevent duplicate account creation - existingAccountData, err := h.accountFileHandler.ReadAccountData() - if existingAccountData != nil { - return res, err - } + // existingAccountData, err := h.accountFileHandler.ReadAccountData() + // if existingAccountData != nil { + // return res, err + // } accountResp, err := h.accountService.CreateAccount() if err != nil { @@ -190,14 +190,7 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou if err != nil { return res, err } - accountPIN := string(input) - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } - // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) @@ -205,18 +198,11 @@ func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resou } res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) - //accountData["AccountPIN"] = accountPIN key := []byte(AccountPin) value := []byte(accountPIN) h.db.Store(key, value, true) - - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } - return res, nil } @@ -261,13 +247,12 @@ func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (res if err != nil { return res, err } - - accountData, err := h.accountFileHandler.ReadAccountData() + storedpin, err := h.db.Fetch([]byte(AccountPin)) if err != nil { return res, err } - if bytes.Equal(input, []byte(accountData["AccountPIN"])) { + if bytes.Equal(input, storedpin) { res.FlagSet = []uint32{flags["flag_valid_pin"]} res.FlagReset = []uint32{flags["flag_pin_mismatch"]} res.FlagSet = append(res.FlagSet, flags["flag_pin_set"]) From 3a3ddc9922d237035d53662b47445dd3707c401d Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Mon, 2 Sep 2024 21:06:42 +0300 Subject: [PATCH 23/37] Use FlagParser on main.go and delete the flags.go file --- cmd/main.go | 60 ++++++++++++++++++++++++++++------------ internal/models/flags.go | 22 --------------- 2 files changed, 42 insertions(+), 40 deletions(-) delete mode 100644 internal/models/flags.go diff --git a/cmd/main.go b/cmd/main.go index 68b468a..ea38272 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,13 +7,13 @@ import ( "os" "path" + "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/engine" "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/ussd" - "git.grassecon.net/urdt/ussd/internal/models" ) var ( @@ -35,22 +35,46 @@ func main() { ctx := context.Background() st := state.NewState(16) st.UseDebug() - state.FlagDebugger.Register(models.USERFLAG_LANGUAGE_SET, "LANGUAGE_CHANGE") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATED, "ACCOUNT_CREATED") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_SUCCESS, "ACCOUNT_SUCCESS") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_PENDING, "ACCOUNT_PENDING") - state.FlagDebugger.Register(models.USERFLAG_INCORRECTPIN, "INCORRECTPIN") - state.FlagDebugger.Register(models.USERFLAG_INCORRECTDATEFORMAT, "INVALIDDATEFORMAT") - state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT, "INVALIDRECIPIENT") - state.FlagDebugger.Register(models.USERFLAG_PINMISMATCH, "PINMISMATCH") - state.FlagDebugger.Register(models.USERFLAG_PIN_SET, "PIN_SET") - state.FlagDebugger.Register(models.USERFLAG_INVALID_RECIPIENT_WITH_INVITE, "INVALIDRECIPIENT_WITH_INVITE") - state.FlagDebugger.Register(models.USERFLAG_INVALID_AMOUNT, "INVALIDAMOUNT") - state.FlagDebugger.Register(models.USERFLAG_ALLOW_UPDATE, "UNLOCKFORUPDATE") - state.FlagDebugger.Register(models.USERFLAG_VALIDPIN, "VALIDPIN") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_AUTHORIZED, "ACCOUNT_AUTHORIZED") - state.FlagDebugger.Register(models.USERFLAG_ACCOUNT_CREATION_FAILED, "ACCOUNT_CREATION_FAILED") - state.FlagDebugger.Register(models.USERFLAG_SINGLE_EDIT, "SINGLEEDIT") + + // Initialize the FlagParser + pfp := path.Join(scriptDir, "pp.csv") + parser := asm.NewFlagParser() + + // Load flags from the pp.csv file + _, err := parser.Load(pfp) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to load flags: %v\n", err) + os.Exit(1) + } + + // Register all flags loaded from pp.csv + flagKeys := []string{ + "flag_language_set", + "flag_account_created", + "flag_account_creation_failed", + "flag_account_pending", + "flag_account_success", + "flag_pin_mismatch", + "flag_pin_set", + "flag_account_authorized", + "flag_invalid_recipient", + "flag_invalid_recipient_with_invite", + "flag_invalid_amount", + "flag_incorrect_pin", + "flag_valid_pin", + "flag_allow_update", + "flag_single_edit", + "flag_incorrect_date_format", + } + + for _, key := range flagKeys { + id, err := parser.GetFlag(key) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to get flag %s: %v\n", key, err) + continue + } + state.FlagDebugger.Register(id, key) + } rfs := resource.NewFsResource(scriptDir) ca := cache.NewCache() @@ -60,7 +84,7 @@ func main() { } dp := path.Join(scriptDir, ".state") - err := os.MkdirAll(dp, 0700) + err = os.MkdirAll(dp, 0700) if err != nil { fmt.Fprintf(os.Stderr, "state dir create exited with error: %v\n", err) os.Exit(1) diff --git a/internal/models/flags.go b/internal/models/flags.go deleted file mode 100644 index b64222c..0000000 --- a/internal/models/flags.go +++ /dev/null @@ -1,22 +0,0 @@ -package models - -import "git.defalsify.org/vise.git/state" - -const ( - USERFLAG_LANGUAGE_SET = iota + state.FLAG_USERSTART - USERFLAG_ACCOUNT_CREATED - USERFLAG_ACCOUNT_CREATION_FAILED - USERFLAG_ACCOUNT_PENDING - USERFLAG_ACCOUNT_SUCCESS - USERFLAG_PINMISMATCH - USERFLAG_PIN_SET - USERFLAG_ACCOUNT_AUTHORIZED - USERFLAG_INVALID_RECIPIENT - USERFLAG_INVALID_RECIPIENT_WITH_INVITE - USERFLAG_INVALID_AMOUNT - USERFLAG_INCORRECTPIN - USERFLAG_VALIDPIN - USERFLAG_ALLOW_UPDATE - USERFLAG_SINGLE_EDIT - USERFLAG_INCORRECTDATEFORMAT -) From d4c4db09f3303199566f1f5575602c5c6aedca4a Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 21:09:39 +0300 Subject: [PATCH 24/37] load profile data from gdbm --- internal/handlers/ussd/menuhandler.go | 185 ++++++++++---------------- 1 file changed, 68 insertions(+), 117 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 3ae0f28..d10b551 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -277,24 +277,11 @@ func codeFromCtx(ctx context.Context) string { // SaveFirstname updates the first name in a JSON data file with the provided input. func (h *Handlers) SaveFirstname(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } if len(input) > 0 { name := string(input) - //accountData["FirstName"] = name - key := []byte(FirstName) value := []byte(name) - h.db.Store(key, value, true) - - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } } return res, nil @@ -307,7 +294,6 @@ func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) secondname := string(input) key := []byte(FamilyName) value := []byte(secondname) - h.db.Store(key, value, true) } @@ -317,19 +303,12 @@ func (h *Handlers) SaveFamilyname(cxt context.Context, sym string, input []byte) // SaveYOB updates the Year of Birth(YOB) in a JSON data file with the provided input. func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - yob := string(input) if len(yob) == 4 { yob := string(input) - //accountData["YOB"] = yob key := []byte(YearOfBirth) value := []byte(yob) - h.db.Store(key, value, true) - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } } return res, nil @@ -338,12 +317,6 @@ func (h *Handlers) SaveYob(cxt context.Context, sym string, input []byte) (resou // SaveLocation updates the location in a JSON data file with the provided input. func (h *Handlers) SaveLocation(cxt context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } - if len(input) > 0 { location := string(input) key := []byte(Location) @@ -360,7 +333,6 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re res := resource.Result{} if len(input) > 0 { gender := string(input) - switch gender { case "1": gender = "Male" @@ -369,16 +341,9 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re case "3": gender = "Unspecified" } - //accountData["Gender"] = gender key := []byte(Gender) value := []byte(gender) - h.db.Store(key, value, true) - - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } } return res, nil } @@ -386,24 +351,11 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re // SaveOfferings updates the offerings(goods and services provided by the user) in a JSON data file with the provided input. func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } - if len(input) > 0 { offerings := string(input) - //accountData["Offerings"] = offerings key := []byte(Offerings) value := []byte(offerings) - h.db.Store(key, value, true) - - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } } return res, nil } @@ -441,18 +393,11 @@ func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input // CheckIdentifier retrieves the PublicKey from the JSON data file. func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err } - res.Content = string(publicKey) - return res, nil } @@ -460,21 +405,12 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte // It sets the required flags that control the flow. func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags flagKeys := []string{"flag_incorrect_pin", "flag_account_authorized", "flag_allow_update"} flags, err := h.PreloadFlags(flagKeys) if err != nil { return res, err } - - // pin := string(input) - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } - storedpin, err := h.db.Fetch([]byte(AccountPin)) if err == nil { if len(input) == 4 { @@ -526,11 +462,6 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b if err != nil { return res, err } - - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } trackingId, err := h.db.Fetch([]byte(TrackingIdKey)) if err != nil { @@ -545,9 +476,12 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b } - //accountData["Status"] = status - err = h.db.Store(toBytes(TrackingIdKey), toBytes(status), true) + err = h.db.Store(toBytes(AccountStatus), toBytes(status), true) + if err != nil { + return res, nil + } + err = h.db.Store(toBytes(TrackingIdKey), toBytes(status), true) if err != nil { return res, nil } @@ -828,39 +762,6 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } -// GetProfileInfo retrieves and formats the profile information of a user from a JSON data file. -func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { - res := resource.Result{} - var age string - accountData, err := h.accountFileHandler.ReadAccountData() - if err != nil { - return res, err - } - var name string - if accountData["FirstName"] == "Not provided" || accountData["FamilyName"] == "Not provided" { - name = "Not provided" - } else { - name = accountData["FirstName"] + " " + accountData["FamilyName"] - } - - gender := accountData["Gender"] - yob := accountData["YOB"] - location := accountData["Location"] - offerings := accountData["Offerings"] - if yob == "Not provided" { - age = "Not provided" - } else { - ageInt, err := strconv.Atoi(yob) - if err != nil { - return res, nil - } - age = strconv.Itoa(utils.CalculateAgeWithYOB(ageInt)) - } - formattedData := fmt.Sprintf("Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", name, gender, age, location, offerings) - res.Content = formattedData - return res, nil -} - // GetSender retrieves the public key from a JSON data file. func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} @@ -879,13 +780,10 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res // GetAmount retrieves the amount from a JSON data file. func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - //accountData, err := h.accountFileHandler.ReadAccountData() amount, err := h.db.Fetch([]byte(Amount)) if err != nil { return res, err } - res.Content = string(amount) return res, nil @@ -906,10 +804,6 @@ func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - // accountData, err := h.accountFileHandler.ReadAccountData() - // if err != nil { - // return res, err - // } publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err @@ -930,12 +824,6 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") - // Preload the required flags - // flagKeys := []string{"flag_invalid_recipient"} - // flags, err := h.PreloadFlags(flagKeys) - // if err != nil { - // return res, err - // } // TODO // Use the amount, recipient and sender to call the API and initialize the transaction @@ -962,3 +850,66 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, nil } + +// GetProfileInfo retrieves and formats the profile information of a user from a JSON data file. +func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { + res := resource.Result{} + + // Define default values + defaultValue := "Not provided" + name := defaultValue + familyName := defaultValue + yob := defaultValue + gender := defaultValue + location := defaultValue + offerings := defaultValue + + // Fetch data using a map for better organization + dataKeys := map[string]*string{ + FirstName: &name, + FamilyName: &familyName, + YearOfBirth: &yob, + Location: &location, + Gender: &gender, + Offerings: &offerings, + } + + // Iterate over keys and fetch values + //iter := h.db.Iterator() + next := h.db.Iterator() + //defer iter.Close() // Ensure the iterator is closed + for key, err := next(); err == nil; key, err = next() { + if valuePointer, ok := dataKeys[string(key)]; ok { + value, fetchErr := h.db.Fetch(key) + if fetchErr == nil { + *valuePointer = string(value) + } + } + } + + // Construct the full name + if familyName != defaultValue { + if name == defaultValue { + name = familyName + } else { + name = name + " " + familyName + } + } + + // Calculate age from year of birth + var age string + if yob != defaultValue { + yobInt, err := strconv.Atoi(yob) + if err != nil { + return res, fmt.Errorf("invalid year of birth: %v", err) + } + age = strconv.Itoa(utils.CalculateAgeWithYOB(yobInt)) + } else { + age = defaultValue + } + + // Format the result + formattedData := fmt.Sprintf("Name: %s\nGender: %s\nAge: %s\nLocation: %s\nYou provide: %s\n", name, gender, age, location, offerings) + res.Content = formattedData + return res, nil +} From c365eaa1d1a0d4049b6225c329c6ae1a6c132074 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Mon, 2 Sep 2024 21:24:16 +0300 Subject: [PATCH 25/37] remove unused code --- internal/handlers/ussd/menuhandler.go | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d10b551..ebcc7f3 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -140,25 +140,12 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r // sets the default values and flags func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags flagKeys := []string{"flag_account_created", "flag_account_creation_failed"} flags, err := h.PreloadFlags(flagKeys) if err != nil { return res, err } - - // err = h.accountFileHandler.EnsureFileExists() - // if err != nil { - // return res, err - // } - - // if an account exists, return to prevent duplicate account creation - // existingAccountData, err := h.accountFileHandler.ReadAccountData() - // if existingAccountData != nil { - // return res, err - // } - accountResp, err := h.accountService.CreateAccount() if err != nil { res.FlagSet = append(res.FlagSet, flags["flag_account_creation_failed"]) @@ -493,12 +480,6 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b res.FlagReset = append(res.FlagSet, flags["flag_account_success"]) res.FlagSet = append(res.FlagReset, flags["flag_account_pending"]) } - - // err = h.accountFileHandler.WriteAccountData(accountData) - // if err != nil { - // return res, err - // } - return res, nil } @@ -851,7 +832,7 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] return res, nil } -// GetProfileInfo retrieves and formats the profile information of a user from a JSON data file. +// GetProfileInfo retrieves and formats the profile information of a user from a Gdbm backed storage. func (h *Handlers) GetProfileInfo(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} From 560714838f20dbbd6726312fe5a8e226cb8a44f8 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Sep 2024 11:51:14 +0300 Subject: [PATCH 26/37] fix menu termination on invalid pin entry --- services/registration/view_profile.vis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/registration/view_profile.vis b/services/registration/view_profile.vis index 2d0a508..a7ffee4 100644 --- a/services/registration/view_profile.vis +++ b/services/registration/view_profile.vis @@ -1,6 +1,6 @@ LOAD get_profile_info 0 MAP get_profile_info -LOAD reset_incorrect 0 +LOAD reset_incorrect 6 CATCH incorrect_pin flag_incorrect_pin 1 CATCH pin_entry flag_account_authorized 0 MOUT back 0 From 0c360c0cc4cfa8872336b2add44b626f5eb74487 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Sep 2024 13:19:32 +0300 Subject: [PATCH 27/37] remove panics after failed execution --- internal/handlers/ussd/menuhandler.go | 49 ++++++++++++++++----------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index ebcc7f3..cb2a913 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -41,6 +41,7 @@ const ( Offerings = "OFFERINGS" Recipient = "RECIPIENT" Amount = "AMOUNT" + AccountCreated = "ACCOUNTCREATED" ) func toBytes(s string) []byte { @@ -146,27 +147,37 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) if err != nil { return res, err } - accountResp, err := h.accountService.CreateAccount() + _, err = h.db.Fetch([]byte(AccountCreated)) if err != nil { - res.FlagSet = append(res.FlagSet, flags["flag_account_creation_failed"]) - return res, err - } - data := map[string]string{ - TrackingIdKey: accountResp.Result.TrackingId, - PublicKeyKey: accountResp.Result.PublicKey, - CustodialIdKey: accountResp.Result.CustodialId.String(), - } + if errors.Is(err, gdbm.ErrItemNotFound) { + accountResp, err := h.accountService.CreateAccount() + if err != nil { + res.FlagSet = append(res.FlagSet, flags["flag_account_creation_failed"]) + return res, err + } + data := map[string]string{ + TrackingIdKey: accountResp.Result.TrackingId, + PublicKeyKey: accountResp.Result.PublicKey, + CustodialIdKey: accountResp.Result.CustodialId.String(), + } - for key, value := range data { - err := h.db.Store(toBytes(key), toBytes(value), true) - if err != nil { + for key, value := range data { + err := h.db.Store(toBytes(key), toBytes(value), true) + if err != nil { + return res, err + } + } + key := []byte(AccountCreated) + value := []byte("1") + h.db.Store(key, value, true) + res.FlagSet = append(res.FlagSet, flags["flag_account_created"]) + return res, err + } else { return res, err - } + } else { + return res, nil } - - res.FlagSet = append(res.FlagSet, flags["flag_account_created"]) - return res, err } // SavePin persists the user's PIN choice into the filesystem @@ -609,11 +620,11 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - panic(err) + return res,err } err = h.db.Delete([]byte(Recipient)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - panic(err) + return res,err } res.FlagReset = append(res.FlagReset, flags["flag_invalid_recipient"], flags["flag_invalid_recipient_with_invite"]) @@ -634,7 +645,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - panic(err) + return res,err } res.FlagReset = append(res.FlagReset, flags["flag_invalid_amount"]) From ab6433168a2c4d73e316b98129e91200788c8478 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Sep 2024 15:02:31 +0300 Subject: [PATCH 28/37] Resolve bug on transaction confirmation PIN --- internal/handlers/ussd/menuhandler.go | 3 ++- services/registration/transaction_initiated.vis | 4 +++- services/registration/transaction_pin.vis | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index cb2a913..2491d5f 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -837,9 +837,10 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] account_authorized_flag, err := h.parser.GetFlag("flag_account_authorized") if err != nil { - res.FlagReset = append(res.FlagReset, account_authorized_flag) + return res, nil } + res.FlagReset = append(res.FlagReset, account_authorized_flag) return res, nil } diff --git a/services/registration/transaction_initiated.vis b/services/registration/transaction_initiated.vis index b1042f5..3b8b9f6 100644 --- a/services/registration/transaction_initiated.vis +++ b/services/registration/transaction_initiated.vis @@ -1,4 +1,6 @@ -LOAD reset_incorrect 0 +LOAD reset_incorrect 6 +CATCH incorrect_pin flag_incorrect_pin 1 +CATCH _ flag_account_authorized 0 LOAD get_amount 10 MAP get_amount RELOAD get_recipient diff --git a/services/registration/transaction_pin.vis b/services/registration/transaction_pin.vis index 878104c..cadbdcf 100644 --- a/services/registration/transaction_pin.vis +++ b/services/registration/transaction_pin.vis @@ -6,7 +6,7 @@ MAP get_sender MOUT back 0 MOUT quit 9 HALT -LOAD authorize_account 1 +LOAD authorize_account 6 RELOAD authorize_account CATCH incorrect_pin flag_incorrect_pin 1 INCMP _ 0 From f90fde90fb70b3352f5bd5d5a110149f064c2ecc Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Sep 2024 15:04:09 +0300 Subject: [PATCH 29/37] Register flags in debugger from the csv --- cmd/main.go | 60 ++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index ea38272..a696850 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,12 +2,14 @@ package main import ( "context" + "encoding/csv" "flag" "fmt" + "io" "os" "path" + "strconv" - "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/cache" "git.defalsify.org/vise.git/engine" "git.defalsify.org/vise.git/persist" @@ -36,44 +38,40 @@ func main() { st := state.NewState(16) st.UseDebug() - // Initialize the FlagParser pfp := path.Join(scriptDir, "pp.csv") - parser := asm.NewFlagParser() - - // Load flags from the pp.csv file - _, err := parser.Load(pfp) + file, err := os.Open(pfp) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to load flags: %v\n", err) + fmt.Fprintf(os.Stderr, "Failed to open CSV file: %v\n", err) os.Exit(1) } + defer file.Close() + reader := csv.NewReader(file) - // Register all flags loaded from pp.csv - flagKeys := []string{ - "flag_language_set", - "flag_account_created", - "flag_account_creation_failed", - "flag_account_pending", - "flag_account_success", - "flag_pin_mismatch", - "flag_pin_set", - "flag_account_authorized", - "flag_invalid_recipient", - "flag_invalid_recipient_with_invite", - "flag_invalid_amount", - "flag_incorrect_pin", - "flag_valid_pin", - "flag_allow_update", - "flag_single_edit", - "flag_incorrect_date_format", - } - - for _, key := range flagKeys { - id, err := parser.GetFlag(key) + // Iterate through the CSV records and register the flags + for { + record, err := reader.Read() if err != nil { - fmt.Fprintf(os.Stderr, "Failed to get flag %s: %v\n", key, err) + if err == io.EOF { + break + } + fmt.Fprintf(os.Stderr, "Error reading CSV file: %v\n", err) + os.Exit(1) + } + + // Ensure the record starts with "flag" and has at least 3 columns + if len(record) < 3 || record[0] != "flag" { continue } - state.FlagDebugger.Register(id, key) + + flagName := record[1] + flagValue, err := strconv.Atoi(record[2]) + if err != nil { + fmt.Fprintf(os.Stderr, "Failed to convert flag value %s to integer: %v\n", record[2], err) + continue + } + + // Register the flag + state.FlagDebugger.Register(uint32(flagValue), flagName) } rfs := resource.NewFsResource(scriptDir) From fee51edadeae7d685da7c899c1805c4f4711203d Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Sep 2024 15:07:49 +0300 Subject: [PATCH 30/37] append sessionid to the gdbm file --- cmd/main.go | 2 +- internal/handlers/ussd/menuhandler.go | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index ea38272..2ecdd12 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -107,7 +107,7 @@ func main() { fp := path.Join(dp, sessionId) - ussdHandlers, err := ussd.NewHandlers(fp, &st) + ussdHandlers, err := ussd.NewHandlers(fp, &st,sessionId) if err != nil { fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index cb2a913..4544f2c 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -24,7 +24,7 @@ import ( var ( scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") - dbFile = path.Join(scriptDir, "vise.gdbm") + //dbFile = path.Join(scriptDir, "userdata.gdbm") ) const ( @@ -65,8 +65,9 @@ type Handlers struct { accountService server.AccountServiceInterface } -func NewHandlers(dir string, st *state.State) (*Handlers, error) { - db, err := gdbm.Open(dbFile, gdbm.ModeWrcreat) +func NewHandlers(dir string, st *state.State, sessionId string) (*Handlers, error) { + filename := path.Join(scriptDir, sessionId+"_userdata.gdbm") + db, err := gdbm.Open(filename, gdbm.ModeWrcreat) if err != nil { panic(err) } @@ -425,9 +426,12 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } + } else { + res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) } } else if errors.Is(err, gdbm.ErrItemNotFound) { - //PIN not set yet + return res, err } else { return res, err } @@ -620,11 +624,11 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - return res,err + return res, err } err = h.db.Delete([]byte(Recipient)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - return res,err + return res, err } res.FlagReset = append(res.FlagReset, flags["flag_invalid_recipient"], flags["flag_invalid_recipient_with_invite"]) @@ -645,7 +649,7 @@ func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input err = h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { - return res,err + return res, err } res.FlagReset = append(res.FlagReset, flags["flag_invalid_amount"]) From a07a8703f4bf7ec918210b9fd0b7087ff46fb663 Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Sep 2024 15:13:55 +0300 Subject: [PATCH 31/37] remove flag setting invalid pin entry --- 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 34228db..912bd17 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -426,9 +426,6 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) return res, nil } - } else { - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) } } else if errors.Is(err, gdbm.ErrItemNotFound) { return res, err From 99d18322b28ecfd660d74b6fd1ca4f237b871c10 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Sep 2024 15:34:19 +0300 Subject: [PATCH 32/37] Append to the correct flag set/reset --- internal/handlers/ussd/menuhandler.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 912bd17..6ed2c4e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -219,13 +219,13 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b switch menuOption { case "2": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "3": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "4": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) default: res.FlagReset = append(res.FlagReset, flags["flag_single_edit"]) @@ -489,8 +489,8 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b res.FlagSet = append(res.FlagSet, flags["flag_account_success"]) res.FlagReset = append(res.FlagReset, flags["flag_account_pending"]) } else { - res.FlagReset = append(res.FlagSet, flags["flag_account_success"]) - res.FlagSet = append(res.FlagReset, flags["flag_account_pending"]) + res.FlagReset = append(res.FlagReset, flags["flag_account_success"]) + res.FlagSet = append(res.FlagSet, flags["flag_account_pending"]) } return res, nil } From ee4db50e00e10f6384a1653f5f71720dfddc24bc Mon Sep 17 00:00:00 2001 From: Carlosokumu Date: Tue, 3 Sep 2024 17:59:03 +0300 Subject: [PATCH 33/37] remove unused code --- internal/handlers/ussd/menuhandler.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 912bd17..87a2a3e 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -24,7 +24,6 @@ import ( var ( scriptDir = path.Join("services", "registration") translationDir = path.Join(scriptDir, "locale") - //dbFile = path.Join(scriptDir, "userdata.gdbm") ) const ( @@ -219,13 +218,13 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b switch menuOption { case "2": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "3": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) case "4": - res.FlagReset = append(res.FlagSet, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) default: res.FlagReset = append(res.FlagReset, flags["flag_single_edit"]) @@ -755,11 +754,9 @@ func (h *Handlers) GetRecipient(ctx context.Context, sym string, input []byte) ( return res, nil } -// GetSender retrieves the public key from a JSON data file. +// GetSender retrieves the public key from the Gdbm Db func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - - //accountData, err := h.accountFileHandler.ReadAccountData() publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) if err != nil { return res, err @@ -770,7 +767,7 @@ func (h *Handlers) GetSender(ctx context.Context, sym string, input []byte) (res return res, nil } -// GetAmount retrieves the amount from a JSON data file. +// GetAmount retrieves the amount from teh Gdbm Db func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} amount, err := h.db.Fetch([]byte(Amount)) From 5105e902f1419953b7f1c9c88c687c243e34c1bf Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Sep 2024 19:06:37 +0300 Subject: [PATCH 34/37] Process flags once and make them available across functions --- internal/handlers/ussd/menuhandler.go | 279 +++++++++++--------------- 1 file changed, 117 insertions(+), 162 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d66a87d..d7a98ef 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -9,6 +9,7 @@ import ( "regexp" "strconv" "strings" + "sync" "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/engine" @@ -52,6 +53,33 @@ type FSData struct { St *state.State } +// FlagManager handles centralized flag management +type FlagManager struct { + parser *asm.FlagParser + mu sync.RWMutex +} + +// NewFlagManager creates a new FlagManager instance +func NewFlagManager(csvPath string) (*FlagManager, error) { + parser := asm.NewFlagParser() + _, err := parser.Load(csvPath) + if err != nil { + return nil, fmt.Errorf("failed to load flag parser: %v", err) + } + + return &FlagManager{ + parser: parser, + }, nil +} + +// GetFlag retrieves a flag value by its label +func (fm *FlagManager) GetFlag(label string) (uint32, error) { + fm.mu.RLock() + defer fm.mu.RUnlock() + + return fm.parser.GetFlag(label) +} + type FlagParserInterface interface { GetFlag(key string) (uint32, error) } @@ -59,7 +87,7 @@ type FlagParserInterface interface { type Handlers struct { fs *FSData db *gdbm.Database - parser FlagParserInterface + flagManager *FlagManager accountFileHandler utils.AccountFileHandlerInterface accountService server.AccountServiceInterface } @@ -71,10 +99,9 @@ func NewHandlers(dir string, st *state.State, sessionId string) (*Handlers, erro panic(err) } pfp := path.Join(scriptDir, "pp.csv") - parser := asm.NewFlagParser() - _, err = parser.Load(pfp) + flagManager, err := NewFlagManager(pfp) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create flag manager: %v", err) } return &Handlers{ db: db, @@ -82,7 +109,7 @@ func NewHandlers(dir string, st *state.State, sessionId string) (*Handlers, erro Path: dir, St: st, }, - parser: parser, + flagManager: flagManager, accountFileHandler: utils.NewAccountFileHandler(dir + "_data"), accountService: &server.AccountService{}, }, nil @@ -100,7 +127,7 @@ func isValidPIN(pin string) bool { func (h *Handlers) PreloadFlags(flagKeys []string) (map[string]uint32, error) { flags := make(map[string]uint32) for _, key := range flagKeys { - flag, err := h.parser.GetFlag(key) + flag, err := h.flagManager.GetFlag(key) if err != nil { return nil, err } @@ -113,13 +140,6 @@ func (h *Handlers) PreloadFlags(flagKeys []string) (map[string]uint32, error) { func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_language_set"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } - inputStr := string(input) switch inputStr { case "0": @@ -131,7 +151,11 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r default: } - res.FlagSet = append(res.FlagSet, flags["flag_language_set"]) + languageSetFlag, err := h.flagManager.GetFlag("flag_language_set") + if err != nil { + return res, err + } + res.FlagSet = append(res.FlagSet, languageSetFlag) return res, nil } @@ -141,18 +165,14 @@ func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (r // sets the default values and flags func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_account_created", "flag_account_creation_failed"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } - _, err = h.db.Fetch([]byte(AccountCreated)) + + _, err := h.db.Fetch([]byte(AccountCreated)) if err != nil { if errors.Is(err, gdbm.ErrItemNotFound) { accountResp, err := h.accountService.CreateAccount() if err != nil { - res.FlagSet = append(res.FlagSet, flags["flag_account_creation_failed"]) + flag_account_creation_failed, _ := h.flagManager.GetFlag("flag_account_creation_failed") + res.FlagSet = append(res.FlagSet, flag_account_creation_failed) return res, err } data := map[string]string{ @@ -170,7 +190,8 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) key := []byte(AccountCreated) value := []byte("1") h.db.Store(key, value, true) - res.FlagSet = append(res.FlagSet, flags["flag_account_created"]) + flag_account_created, _ := h.flagManager.GetFlag("flag_account_created") + res.FlagSet = append(res.FlagSet, flag_account_created) return res, err } else { return res, err @@ -183,19 +204,17 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte) // SavePin persists the user's PIN choice into the filesystem func (h *Handlers) SavePin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - flagKeys := []string{"flag_incorrect_pin"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") + accountPIN := string(input) // Validate that the PIN is a 4-digit number if !isValidPIN(accountPIN) { - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) + res.FlagSet = append(res.FlagSet, flag_incorrect_pin) return res, nil } - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) key := []byte(AccountPin) value := []byte(accountPIN) @@ -209,25 +228,21 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b res := resource.Result{} menuOption := string(input) - // Preload the required flags - flagKeys := []string{"flag_allow_update", "flag_single_edit"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit") switch menuOption { case "2": - res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) - res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) case "3": - res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) - res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) case "4": - res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) - res.FlagSet = append(res.FlagSet, flags["flag_single_edit"]) + res.FlagReset = append(res.FlagReset, flag_allow_update) + res.FlagSet = append(res.FlagSet, flag_single_edit) default: - res.FlagReset = append(res.FlagReset, flags["flag_single_edit"]) + res.FlagReset = append(res.FlagReset, flag_single_edit) } return res, nil @@ -239,23 +254,20 @@ func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []b func (h *Handlers) VerifyPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_valid_pin", "flag_pin_mismatch", "flag_pin_set"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_valid_pin, _ := h.flagManager.GetFlag("flag_valid_pin") + flag_pin_mismatch, _ := h.flagManager.GetFlag("flag_pin_mismatch") + flag_pin_set, _ := h.flagManager.GetFlag("flag_pin_set") AccountPin, err := h.db.Fetch([]byte(AccountPin)) if err != nil { return res, err } if bytes.Equal(input, AccountPin) { - res.FlagSet = []uint32{flags["flag_valid_pin"]} - res.FlagReset = []uint32{flags["flag_pin_mismatch"]} - res.FlagSet = append(res.FlagSet, flags["flag_pin_set"]) + res.FlagSet = []uint32{flag_valid_pin} + res.FlagReset = []uint32{flag_pin_mismatch} + res.FlagSet = append(res.FlagSet, flag_pin_set) } else { - res.FlagSet = []uint32{flags["flag_pin_mismatch"]} + res.FlagSet = []uint32{flag_pin_mismatch} } return res, nil @@ -362,14 +374,9 @@ func (h *Handlers) SaveOfferings(ctx context.Context, sym string, input []byte) func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_allow_update"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") - res.FlagReset = append(res.FlagReset, flags["flag_allow_update"]) + res.FlagReset = append(res.FlagReset, flag_allow_update) return res, nil } @@ -377,14 +384,9 @@ func (h *Handlers) ResetAllowUpdate(ctx context.Context, sym string, input []byt func (h *Handlers) ResetAccountAuthorized(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_account_authorized"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } @@ -403,26 +405,25 @@ func (h *Handlers) CheckIdentifier(ctx context.Context, sym string, input []byte // It sets the required flags that control the flow. func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_incorrect_pin", "flag_account_authorized", "flag_allow_update"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") + flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update") + storedpin, err := h.db.Fetch([]byte(AccountPin)) if err == nil { if len(input) == 4 { if bytes.Equal(input, storedpin) { - if h.fs.St.MatchFlag(flags["flag_account_authorized"], false) { - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) - res.FlagSet = append(res.FlagSet, flags["flag_allow_update"], flags["flag_account_authorized"]) + if h.fs.St.MatchFlag(flag_account_authorized, false) { + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) + res.FlagSet = append(res.FlagSet, flag_allow_update, flag_account_authorized) } else { - res.FlagSet = append(res.FlagSet, flags["flag_allow_update"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + res.FlagSet = append(res.FlagSet, flag_allow_update) + res.FlagReset = append(res.FlagReset, flag_account_authorized) } } else { - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_pin"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + res.FlagSet = append(res.FlagSet, flag_incorrect_pin) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } } @@ -438,14 +439,9 @@ func (h *Handlers) Authorize(ctx context.Context, sym string, input []byte) (res func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_incorrect_pin"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_incorrect_pin, _ := h.flagManager.GetFlag("flag_incorrect_pin") - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_pin"]) + res.FlagReset = append(res.FlagReset, flag_incorrect_pin) return res, nil } @@ -454,12 +450,9 @@ func (h *Handlers) ResetIncorrectPin(ctx context.Context, sym string, input []by func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_account_success", "flag_account_pending"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_account_success, _ := h.flagManager.GetFlag("flag_account_success") + flag_account_pending, _ := h.flagManager.GetFlag("flag_account_pending") + trackingId, err := h.db.Fetch([]byte(TrackingIdKey)) if err != nil { @@ -485,11 +478,11 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b } if status == "SUCCESS" { - res.FlagSet = append(res.FlagSet, flags["flag_account_success"]) - res.FlagReset = append(res.FlagReset, flags["flag_account_pending"]) + res.FlagSet = append(res.FlagSet, flag_account_success) + res.FlagReset = append(res.FlagReset, flag_account_pending) } else { - res.FlagReset = append(res.FlagReset, flags["flag_account_success"]) - res.FlagSet = append(res.FlagSet, flags["flag_account_pending"]) + res.FlagReset = append(res.FlagReset, flag_account_success) + res.FlagSet = append(res.FlagSet, flag_account_pending) } return res, nil } @@ -498,19 +491,14 @@ func (h *Handlers) CheckAccountStatus(ctx context.Context, sym string, input []b func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_account_authorized"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) l.AddDomain("default") res.Content = l.Get("Thank you for using Sarafu. Goodbye!") - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } @@ -518,25 +506,20 @@ func (h *Handlers) Quit(ctx context.Context, sym string, input []byte) (resource func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_incorrect_date_format"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") date := string(input) - _, err = strconv.Atoi(date) + _, err := strconv.Atoi(date) if err != nil { // If conversion fails, input is not numeric - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_date_format"]) + res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) return res, nil } if len(date) == 4 { - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_date_format"]) + res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) } else { - res.FlagSet = append(res.FlagSet, flags["flag_incorrect_date_format"]) + res.FlagSet = append(res.FlagSet, flag_incorrect_date_format) } return res, nil @@ -546,14 +529,9 @@ func (h *Handlers) VerifyYob(ctx context.Context, sym string, input []byte) (res func (h *Handlers) ResetIncorrectYob(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_incorrect_date_format"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_incorrect_date_format, _ := h.flagManager.GetFlag("flag_incorrect_date_format") - res.FlagReset = append(res.FlagReset, flags["flag_incorrect_date_format"]) + res.FlagReset = append(res.FlagReset, flag_incorrect_date_format) return res, nil } @@ -581,16 +559,12 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by res := resource.Result{} recipient := string(input) - flagKeys := []string{"flag_invalid_recipient"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") if recipient != "0" { // mimic invalid number check if recipient == "000" { - res.FlagSet = append(res.FlagSet, flags["flag_invalid_recipient"]) + res.FlagSet = append(res.FlagSet, flag_invalid_recipient) res.Content = recipient return res, nil @@ -611,14 +585,10 @@ func (h *Handlers) ValidateRecipient(ctx context.Context, sym string, input []by func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flags - flagKeys := []string{"flag_invalid_recipient", "flag_invalid_recipient_with_invite"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_invalid_recipient, _ := h.flagManager.GetFlag("flag_invalid_recipient") + flag_invalid_recipient_with_invite, _ := h.flagManager.GetFlag("flag_invalid_recipient_with_invite") - err = h.db.Delete([]byte(Amount)) + err := h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { return res, err } @@ -627,7 +597,7 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt return res, err } - res.FlagReset = append(res.FlagReset, flags["flag_invalid_recipient"], flags["flag_invalid_recipient_with_invite"]) + res.FlagReset = append(res.FlagReset, flag_invalid_recipient, flag_invalid_recipient_with_invite) return res, nil } @@ -636,19 +606,14 @@ func (h *Handlers) TransactionReset(ctx context.Context, sym string, input []byt func (h *Handlers) ResetTransactionAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_invalid_amount"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") - err = h.db.Delete([]byte(Amount)) + err := h.db.Delete([]byte(Amount)) if err != nil && !errors.Is(err, gdbm.ErrItemNotFound) { return res, err } - res.FlagReset = append(res.FlagReset, flags["flag_invalid_amount"]) + res.FlagReset = append(res.FlagReset, flag_invalid_amount) return res, nil } @@ -676,12 +641,8 @@ func (h *Handlers) MaxAmount(ctx context.Context, sym string, input []byte) (res // it is not more than the current balance. func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_invalid_amount"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + + flag_invalid_amount, _ := h.flagManager.GetFlag("flag_invalid_amount") amountStr := string(input) publicKey, err := h.db.Fetch([]byte(PublicKeyKey)) @@ -711,20 +672,20 @@ func (h *Handlers) ValidateAmount(ctx context.Context, sym string, input []byte) re := regexp.MustCompile(`^(\d+(\.\d+)?)\s*(?:CELO)?$`) matches := re.FindStringSubmatch(strings.TrimSpace(amountStr)) if len(matches) < 2 { - res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } inputAmount, err := strconv.ParseFloat(matches[1], 64) if err != nil { - res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } if inputAmount > balanceValue { - res.FlagSet = append(res.FlagSet, flags["flag_invalid_amount"]) + res.FlagSet = append(res.FlagSet, flag_invalid_amount) res.Content = amountStr return res, nil } @@ -784,12 +745,7 @@ func (h *Handlers) GetAmount(ctx context.Context, sym string, input []byte) (res func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} - // Preload the required flag - flagKeys := []string{"flag_account_authorized"} - flags, err := h.PreloadFlags(flagKeys) - if err != nil { - return res, err - } + flag_account_authorized, _ := h.flagManager.GetFlag("flag_account_authorized") code := codeFromCtx(ctx) l := gotext.NewLocale(translationDir, code) @@ -803,7 +759,7 @@ func (h *Handlers) QuitWithBalance(ctx context.Context, sym string, input []byte return res, nil } res.Content = l.Get("Your account balance is %s", balance) - res.FlagReset = append(res.FlagReset, flags["flag_account_authorized"]) + res.FlagReset = append(res.FlagReset, flag_account_authorized) return res, nil } @@ -832,10 +788,9 @@ func (h *Handlers) InitiateTransaction(ctx context.Context, sym string, input [] res.Content = l.Get("Your request has been sent. %s will receive %s from %s.", string(recipient), string(amount), string(publicKey)) - account_authorized_flag, err := h.parser.GetFlag("flag_account_authorized") - + account_authorized_flag, err := h.flagManager.GetFlag("flag_account_authorized") if err != nil { - return res, nil + return res, err } res.FlagReset = append(res.FlagReset, account_authorized_flag) From 2d7b6bd125c6bbe8a831ad9170aabdc706097631 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Tue, 3 Sep 2024 22:28:48 +0300 Subject: [PATCH 35/37] Removed unused code --- internal/handlers/ussd/menuhandler.go | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index d7a98ef..12ddcde 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -80,10 +80,6 @@ func (fm *FlagManager) GetFlag(label string) (uint32, error) { return fm.parser.GetFlag(label) } -type FlagParserInterface interface { - GetFlag(key string) (uint32, error) -} - type Handlers struct { fs *FSData db *gdbm.Database @@ -124,18 +120,6 @@ func isValidPIN(pin string) bool { return match } -func (h *Handlers) PreloadFlags(flagKeys []string) (map[string]uint32, error) { - flags := make(map[string]uint32) - for _, key := range flagKeys { - flag, err := h.flagManager.GetFlag(key) - if err != nil { - return nil, err - } - flags[key] = flag - } - return flags, nil -} - // SetLanguage sets the language across the menu func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) { res := resource.Result{} From 8ffc5d67ccb00973dccb117fe9ce16466ab29501 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Sep 2024 12:01:51 +0300 Subject: [PATCH 36/37] Removed mutex --- internal/handlers/ussd/menuhandler.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go index 12ddcde..c47f2e0 100644 --- a/internal/handlers/ussd/menuhandler.go +++ b/internal/handlers/ussd/menuhandler.go @@ -9,7 +9,6 @@ import ( "regexp" "strconv" "strings" - "sync" "git.defalsify.org/vise.git/asm" "git.defalsify.org/vise.git/engine" @@ -56,7 +55,6 @@ type FSData struct { // FlagManager handles centralized flag management type FlagManager struct { parser *asm.FlagParser - mu sync.RWMutex } // NewFlagManager creates a new FlagManager instance @@ -74,9 +72,6 @@ func NewFlagManager(csvPath string) (*FlagManager, error) { // GetFlag retrieves a flag value by its label func (fm *FlagManager) GetFlag(label string) (uint32, error) { - fm.mu.RLock() - defer fm.mu.RUnlock() - return fm.parser.GetFlag(label) } From 1957606bc22fccfcdd1697754d1385872af70625 Mon Sep 17 00:00:00 2001 From: alfred-mk Date: Wed, 4 Sep 2024 12:19:34 +0300 Subject: [PATCH 37/37] Added log to show debug the flag loaded --- cmd/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/main.go b/cmd/main.go index 72d1d11..3be8980 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "io" + "log" "os" "path" "strconv" @@ -71,6 +72,7 @@ func main() { } // Register the flag + log.Printf("Registering flagName:%s; flagValue:%v", flagName, flagValue) state.FlagDebugger.Register(uint32(flagValue), flagName) } @@ -105,7 +107,7 @@ func main() { fp := path.Join(dp, sessionId) - ussdHandlers, err := ussd.NewHandlers(fp, &st,sessionId) + ussdHandlers, err := ussd.NewHandlers(fp, &st, sessionId) if err != nil { fmt.Fprintf(os.Stderr, "handler setup failed with error: %v\n", err)