wip-menu-select-fix #78
@ -12,27 +12,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/asm"
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
fsdb "git.defalsify.org/vise.git/db/fs"
|
|
||||||
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
)
|
)
|
||||||
|
|
||||||
type atRequestParser struct {}
|
type atRequestParser struct{}
|
||||||
|
|
||||||
func(arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
||||||
rqv, ok := rq.(*http.Request)
|
rqv, ok := rq.(*http.Request)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", handlers.ErrInvalidRequest
|
return "", handlers.ErrInvalidRequest
|
||||||
@ -49,7 +45,7 @@ func(arp *atRequestParser) GetSessionId(rq any) (string, error) {
|
|||||||
return phoneNumber, nil
|
return phoneNumber, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func(arp *atRequestParser) GetInput(rq any) ([]byte, error) {
|
func (arp *atRequestParser) GetInput(rq any) ([]byte, error) {
|
||||||
rqv, ok := rq.(*http.Request)
|
rqv, ok := rq.(*http.Request)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, handlers.ErrInvalidRequest
|
return nil, handlers.ErrInvalidRequest
|
||||||
@ -68,96 +64,6 @@ func(arp *atRequestParser) GetInput(rq any) ([]byte, error) {
|
|||||||
return []byte(parts[len(parts)-1]), nil
|
return []byte(parts[len(parts)-1]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func getFlags(fp string, debug bool) (*asm.FlagParser, error) {
|
|
||||||
flagParser := asm.NewFlagParser().WithDebug()
|
|
||||||
_, err := flagParser.Load(fp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return flagParser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, userdataStore db.Db) (*ussd.Handlers, error) {
|
|
||||||
|
|
||||||
ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
|
|
||||||
rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
|
|
||||||
rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
|
|
||||||
rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
|
|
||||||
rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
|
|
||||||
rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
|
|
||||||
rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
|
|
||||||
rs.AddLocalFunc("quit", ussdHandlers.Quit)
|
|
||||||
rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
|
|
||||||
rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
|
|
||||||
rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
|
|
||||||
rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
|
|
||||||
rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
|
|
||||||
rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
|
|
||||||
rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
|
|
||||||
rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
|
|
||||||
rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
|
|
||||||
rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
|
|
||||||
rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
|
|
||||||
rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
|
|
||||||
rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
|
|
||||||
rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
|
|
||||||
rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
|
|
||||||
rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
|
|
||||||
rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
|
|
||||||
rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
|
|
||||||
rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
|
|
||||||
rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
|
||||||
rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
|
||||||
rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
|
||||||
rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
|
||||||
rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
|
||||||
rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
|
||||||
rs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
|
||||||
rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
|
||||||
rs.AddLocalFunc("quit_with_help",ussdHandlers.QuitWithHelp)
|
|
||||||
|
|
||||||
return ussdHandlers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDbDir(dbDir string) error {
|
|
||||||
err := os.MkdirAll(dbDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStateStore(dbDir string, ctx context.Context) (db.Db, error) {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "state.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
return store, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "userdata.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
|
|
||||||
store := fsdb.NewFsDb()
|
|
||||||
err := store.Connect(ctx, resourceDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rfs := resource.NewDbResource(store)
|
|
||||||
return rfs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var dbDir string
|
var dbDir string
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
@ -175,15 +81,10 @@ func main() {
|
|||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
flagParser, err := getFlags(pfp, true)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
@ -197,19 +98,21 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := getResource(resourceDir, ctx)
|
menuStorageService := storage.MenuStorageService{}
|
||||||
|
|
||||||
|
rs, err := menuStorageService.GetResource(scriptDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ensureDbDir(dbDir)
|
err = menuStorageService.EnsureDbDir(dbDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
userdataStore := getUserdataDb(dbDir, ctx)
|
userdataStore := menuStorageService.GetUserdataDb(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -221,13 +124,21 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
hl, err := getHandler(flagParser, dbResource, userdataStore)
|
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
||||||
|
lhs.WithDataStore(&userdataStore)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateStore, err := getStateStore(dbDir, ctx)
|
hl, err := lhs.GetHandler()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateStore, err := menuStorageService.GetStateStore(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -238,7 +149,7 @@ func main() {
|
|||||||
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
||||||
sh := httpserver.NewATSessionHandler(bsh)
|
sh := httpserver.NewATSessionHandler(bsh)
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
|
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
|
||||||
Handler: sh,
|
Handler: sh,
|
||||||
}
|
}
|
||||||
s.RegisterOnShutdown(sh.Shutdown)
|
s.RegisterOnShutdown(sh.Shutdown)
|
||||||
|
@ -9,125 +9,32 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/asm"
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
fsdb "git.defalsify.org/vise.git/db/fs"
|
|
||||||
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/resource"
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
)
|
)
|
||||||
|
|
||||||
type asyncRequestParser struct {
|
type asyncRequestParser struct {
|
||||||
sessionId string
|
sessionId string
|
||||||
input []byte
|
input []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func(p *asyncRequestParser) GetSessionId(r any) (string, error) {
|
func (p *asyncRequestParser) GetSessionId(r any) (string, error) {
|
||||||
return p.sessionId, nil
|
return p.sessionId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func(p *asyncRequestParser) GetInput(r any) ([]byte, error) {
|
func (p *asyncRequestParser) GetInput(r any) ([]byte, error) {
|
||||||
return p.input, nil
|
return p.input, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFlags(fp string, debug bool) (*asm.FlagParser, error) {
|
|
||||||
flagParser := asm.NewFlagParser().WithDebug()
|
|
||||||
_, err := flagParser.Load(fp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return flagParser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, userdataStore db.Db) (*ussd.Handlers, error) {
|
|
||||||
|
|
||||||
ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
|
|
||||||
rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
|
|
||||||
rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
|
|
||||||
rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
|
|
||||||
rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
|
|
||||||
rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
|
|
||||||
rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
|
|
||||||
rs.AddLocalFunc("quit", ussdHandlers.Quit)
|
|
||||||
rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
|
|
||||||
rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
|
|
||||||
rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
|
|
||||||
rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
|
|
||||||
rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
|
|
||||||
rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
|
|
||||||
rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
|
|
||||||
rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
|
|
||||||
rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
|
|
||||||
rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
|
|
||||||
rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
|
|
||||||
rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
|
|
||||||
rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
|
|
||||||
rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
|
|
||||||
rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
|
|
||||||
rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
|
|
||||||
rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
|
|
||||||
rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
|
|
||||||
rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
|
|
||||||
rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
|
||||||
rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
|
||||||
rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
|
||||||
rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
|
||||||
rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
|
||||||
rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
|
||||||
rs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
|
||||||
rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
|
||||||
rs.AddLocalFunc("quit_with_help",ussdHandlers.QuitWithHelp)
|
|
||||||
|
|
||||||
return ussdHandlers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDbDir(dbDir string) error {
|
|
||||||
err := os.MkdirAll(dbDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStateStore(dbDir string, ctx context.Context) (db.Db, error) {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "state.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
return store, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "userdata.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
|
|
||||||
store := fsdb.NewFsDb()
|
|
||||||
err := store.Connect(ctx, resourceDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rfs := resource.NewDbResource(store)
|
|
||||||
return rfs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var dbDir string
|
var dbDir string
|
||||||
@ -147,15 +54,10 @@ func main() {
|
|||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
|
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
flagParser, err := getFlags(pfp, true)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
@ -169,19 +71,20 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := getResource(resourceDir, ctx)
|
menuStorageService := storage.MenuStorageService{}
|
||||||
|
rs, err := menuStorageService.GetResource(scriptDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ensureDbDir(dbDir)
|
err = menuStorageService.EnsureDbDir(dbDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
userdataStore := getUserdataDb(dbDir, ctx)
|
userdataStore := menuStorageService.GetUserdataDb(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -193,13 +96,16 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
hl, err := getHandler(flagParser, dbResource, userdataStore)
|
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
||||||
|
lhs.WithDataStore(&userdataStore)
|
||||||
|
|
||||||
|
hl, err := lhs.GetHandler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateStore, err := getStateStore(dbDir, ctx)
|
stateStore, err := menuStorageService.GetStateStore(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -212,7 +118,7 @@ func main() {
|
|||||||
sh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
sh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
||||||
cfg.SessionId = sessionId
|
cfg.SessionId = sessionId
|
||||||
rqs := handlers.RequestSession{
|
rqs := handlers.RequestSession{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Writer: os.Stdout,
|
Writer: os.Stdout,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
}
|
}
|
||||||
@ -248,7 +154,7 @@ func main() {
|
|||||||
fmt.Println("")
|
fmt.Println("")
|
||||||
_, err = fmt.Scanln(&rqs.Input)
|
_, err = fmt.Scanln(&rqs.Input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Errorf("error in input: %v", err)
|
fmt.Errorf("error in input: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
cmd/http/main.go
127
cmd/http/main.go
@ -11,113 +11,20 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/asm"
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
fsdb "git.defalsify.org/vise.git/db/fs"
|
|
||||||
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/resource"
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
)
|
)
|
||||||
|
|
||||||
func getFlags(fp string, debug bool) (*asm.FlagParser, error) {
|
|
||||||
flagParser := asm.NewFlagParser().WithDebug()
|
|
||||||
_, err := flagParser.Load(fp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return flagParser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, userdataStore db.Db) (*ussd.Handlers, error) {
|
|
||||||
|
|
||||||
ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
|
|
||||||
rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
|
|
||||||
rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
|
|
||||||
rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
|
|
||||||
rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
|
|
||||||
rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
|
|
||||||
rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
|
|
||||||
rs.AddLocalFunc("quit", ussdHandlers.Quit)
|
|
||||||
rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
|
|
||||||
rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
|
|
||||||
rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
|
|
||||||
rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
|
|
||||||
rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
|
|
||||||
rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
|
|
||||||
rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
|
|
||||||
rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
|
|
||||||
rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
|
|
||||||
rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
|
|
||||||
rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
|
|
||||||
rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
|
|
||||||
rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
|
|
||||||
rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
|
|
||||||
rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
|
|
||||||
rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
|
|
||||||
rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
|
|
||||||
rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
|
|
||||||
rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
|
|
||||||
rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
|
||||||
rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
|
||||||
rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
|
||||||
rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
|
||||||
rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
|
||||||
rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
|
||||||
rs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
|
||||||
rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
|
||||||
rs.AddLocalFunc("quit_with_help",ussdHandlers.QuitWithHelp)
|
|
||||||
|
|
||||||
return ussdHandlers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDbDir(dbDir string) error {
|
|
||||||
err := os.MkdirAll(dbDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStateStore(dbDir string, ctx context.Context) (db.Db, error) {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "state.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
return store, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "userdata.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
|
|
||||||
store := fsdb.NewFsDb()
|
|
||||||
err := store.Connect(ctx, resourceDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rfs := resource.NewDbResource(store)
|
|
||||||
return rfs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var dbDir string
|
var dbDir string
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
@ -135,15 +42,10 @@ func main() {
|
|||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
flagParser, err := getFlags(pfp, true)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
@ -157,19 +59,20 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := getResource(resourceDir, ctx)
|
menuStorageService := storage.MenuStorageService{}
|
||||||
|
rs, err := menuStorageService.GetResource(scriptDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ensureDbDir(dbDir)
|
err = menuStorageService.EnsureDbDir(dbDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
userdataStore := getUserdataDb(dbDir, ctx)
|
userdataStore := menuStorageService.GetUserdataDb(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -181,13 +84,21 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
hl, err := getHandler(flagParser, dbResource, userdataStore)
|
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
||||||
|
lhs.WithDataStore(&userdataStore)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
stateStore, err := getStateStore(dbDir, ctx)
|
hl, err := lhs.GetHandler()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
stateStore, err := menuStorageService.GetStateStore(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -198,7 +109,7 @@ func main() {
|
|||||||
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
||||||
sh := httpserver.ToSessionHandler(bsh)
|
sh := httpserver.ToSessionHandler(bsh)
|
||||||
s := &http.Server{
|
s := &http.Server{
|
||||||
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
|
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
|
||||||
Handler: sh,
|
Handler: sh,
|
||||||
}
|
}
|
||||||
s.RegisterOnShutdown(sh.Shutdown)
|
s.RegisterOnShutdown(sh.Shutdown)
|
||||||
|
140
cmd/main.go
140
cmd/main.go
@ -7,15 +7,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/asm"
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
fsdb "git.defalsify.org/vise.git/db/fs"
|
|
||||||
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
"git.defalsify.org/vise.git/persist"
|
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -23,106 +19,6 @@ var (
|
|||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
)
|
)
|
||||||
|
|
||||||
func getParser(fp string, debug bool) (*asm.FlagParser, error) {
|
|
||||||
flagParser := asm.NewFlagParser().WithDebug()
|
|
||||||
_, err := flagParser.Load(fp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return flagParser, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHandler(appFlags *asm.FlagParser, rs *resource.DbResource, pe *persist.Persister, userdataStore db.Db) (*ussd.Handlers, error) {
|
|
||||||
|
|
||||||
ussdHandlers, err := ussd.NewHandlers(appFlags, userdataStore)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ussdHandlers = ussdHandlers.WithPersister(pe)
|
|
||||||
rs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
|
|
||||||
rs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
|
|
||||||
rs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
|
|
||||||
rs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
|
|
||||||
rs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
|
|
||||||
rs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
|
|
||||||
rs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
|
|
||||||
rs.AddLocalFunc("quit", ussdHandlers.Quit)
|
|
||||||
rs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
|
|
||||||
rs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
|
|
||||||
rs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
|
|
||||||
rs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
|
|
||||||
rs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
|
|
||||||
rs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
|
|
||||||
rs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
|
|
||||||
rs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
|
|
||||||
rs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
|
|
||||||
rs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
|
|
||||||
rs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
|
|
||||||
rs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
|
|
||||||
rs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
|
|
||||||
rs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
|
|
||||||
rs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
|
|
||||||
rs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
|
|
||||||
rs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
|
|
||||||
rs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
|
|
||||||
rs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
|
|
||||||
rs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
|
||||||
rs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
|
||||||
rs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
|
||||||
rs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
|
||||||
rs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
|
||||||
rs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
|
||||||
rs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
|
||||||
rs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
|
||||||
rs.AddLocalFunc("quit_with_help",ussdHandlers.QuitWithHelp)
|
|
||||||
|
|
||||||
return ussdHandlers, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureDbDir(dbDir string) error {
|
|
||||||
err := os.MkdirAll(dbDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPersister(dbDir string, ctx context.Context) (*persist.Persister, error) {
|
|
||||||
err := ensureDbDir(dbDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "state.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
pr := persist.NewPersister(store)
|
|
||||||
return pr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUserdataDb(dbDir string, ctx context.Context) db.Db {
|
|
||||||
store := gdbmdb.NewGdbmDb()
|
|
||||||
storeFile := path.Join(dbDir, "userdata.gdbm")
|
|
||||||
store.Connect(ctx, storeFile)
|
|
||||||
|
|
||||||
return store
|
|
||||||
}
|
|
||||||
|
|
||||||
func getResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
|
|
||||||
store := fsdb.NewFsDb()
|
|
||||||
err := store.Connect(ctx, resourceDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rfs := resource.NewDbResource(store)
|
|
||||||
return rfs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEngine(cfg engine.Config, rs resource.Resource, pr *persist.Persister) *engine.DefaultEngine {
|
|
||||||
en := engine.NewEngine(cfg, rs)
|
|
||||||
en = en.WithPersister(pr)
|
|
||||||
return en
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var dbDir string
|
var dbDir string
|
||||||
var size uint
|
var size uint
|
||||||
@ -139,11 +35,6 @@ func main() {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
flagParser, err := getParser(pfp, true)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
Root: "root",
|
Root: "root",
|
||||||
@ -152,19 +43,27 @@ func main() {
|
|||||||
FlagCount: uint32(16),
|
FlagCount: uint32(16),
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := getResource(scriptDir, ctx)
|
menuStorageService := storage.MenuStorageService{}
|
||||||
|
|
||||||
|
err := menuStorageService.EnsureDbDir(dbDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pe, err := getPersister(dbDir, ctx)
|
rs, err := menuStorageService.GetResource(scriptDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
store := getUserdataDb(dbDir, ctx)
|
pe, err := menuStorageService.GetPersister(dbDir, ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
userdatastore := menuStorageService.GetUserdataDb(dbDir, ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -176,13 +75,22 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
hl, err := getHandler(flagParser, dbResource, pe, store)
|
lhs, err := handlers.NewLocalHandlerService(pfp, true, dbResource, cfg, rs)
|
||||||
|
lhs.WithDataStore(&userdatastore)
|
||||||
|
lhs.WithPersister(pe)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
en := getEngine(cfg, rs, pe)
|
hl, err := lhs.GetHandler()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
en := lhs.GetEngine()
|
||||||
en = en.WithFirst(hl.Init)
|
en = en.WithFirst(hl.Init)
|
||||||
if debug {
|
if debug {
|
||||||
en = en.WithDebug(nil)
|
en = en.WithDebug(nil)
|
||||||
|
105
internal/handlers/handlerservice.go
Normal file
105
internal/handlers/handlerservice.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.defalsify.org/vise.git/asm"
|
||||||
|
"git.defalsify.org/vise.git/db"
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.defalsify.org/vise.git/persist"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HandlerService interface {
|
||||||
|
GetHandler() (*ussd.Handlers, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParser(fp string, debug bool) (*asm.FlagParser, error) {
|
||||||
|
flagParser := asm.NewFlagParser().WithDebug()
|
||||||
|
_, err := flagParser.Load(fp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return flagParser, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type LocalHandlerService struct {
|
||||||
|
Parser *asm.FlagParser
|
||||||
|
DbRs *resource.DbResource
|
||||||
|
Pe *persist.Persister
|
||||||
|
UserdataStore *db.Db
|
||||||
|
Cfg engine.Config
|
||||||
|
Rs resource.Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalHandlerService(fp string, debug bool, dbResource *resource.DbResource, cfg engine.Config, rs resource.Resource) (*LocalHandlerService, error) {
|
||||||
|
parser, err := getParser(fp, debug)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &LocalHandlerService{
|
||||||
|
Parser: parser,
|
||||||
|
DbRs: dbResource,
|
||||||
|
Cfg: cfg,
|
||||||
|
Rs: rs,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localHandlerService *LocalHandlerService) WithPersister(Pe *persist.Persister) {
|
||||||
|
localHandlerService.Pe = Pe
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localHandlerService *LocalHandlerService) WithDataStore(db *db.Db) {
|
||||||
|
localHandlerService.UserdataStore = db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localHandlerService *LocalHandlerService) GetHandler() (*ussd.Handlers, error) {
|
||||||
|
ussdHandlers, err := ussd.NewHandlers(localHandlerService.Parser, *localHandlerService.UserdataStore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ussdHandlers = ussdHandlers.WithPersister(localHandlerService.Pe)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("set_language", ussdHandlers.SetLanguage)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("create_account", ussdHandlers.CreateAccount)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_pin", ussdHandlers.SavePin)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("verify_pin", ussdHandlers.VerifyPin)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("check_identifier", ussdHandlers.CheckIdentifier)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("check_account_status", ussdHandlers.CheckAccountStatus)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("authorize_account", ussdHandlers.Authorize)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("quit", ussdHandlers.Quit)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("check_balance", ussdHandlers.CheckBalance)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("validate_recipient", ussdHandlers.ValidateRecipient)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("transaction_reset", ussdHandlers.TransactionReset)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("max_amount", ussdHandlers.MaxAmount)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("validate_amount", ussdHandlers.ValidateAmount)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("reset_transaction_amount", ussdHandlers.ResetTransactionAmount)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("get_recipient", ussdHandlers.GetRecipient)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("get_sender", ussdHandlers.GetSender)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("get_amount", ussdHandlers.GetAmount)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("reset_incorrect", ussdHandlers.ResetIncorrectPin)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_firstname", ussdHandlers.SaveFirstname)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_familyname", ussdHandlers.SaveFamilyname)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_gender", ussdHandlers.SaveGender)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_location", ussdHandlers.SaveLocation)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_yob", ussdHandlers.SaveYob)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_offerings", ussdHandlers.SaveOfferings)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("quit_with_balance", ussdHandlers.QuitWithBalance)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("reset_account_authorized", ussdHandlers.ResetAccountAuthorized)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("reset_allow_update", ussdHandlers.ResetAllowUpdate)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("confirm_pin_change", ussdHandlers.ConfirmPinChange)
|
||||||
|
localHandlerService.DbRs.AddLocalFunc("quit_with_help", ussdHandlers.QuitWithHelp)
|
||||||
|
|
||||||
|
return ussdHandlers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (localHandlerService *LocalHandlerService) GetEngine() *engine.DefaultEngine {
|
||||||
|
en := engine.NewEngine(localHandlerService.Cfg, localHandlerService.Rs)
|
||||||
|
en = en.WithPersister(localHandlerService.Pe)
|
||||||
|
return en
|
||||||
|
}
|
@ -29,11 +29,6 @@ var (
|
|||||||
translationDir = path.Join(scriptDir, "locale")
|
translationDir = path.Join(scriptDir, "locale")
|
||||||
)
|
)
|
||||||
|
|
||||||
type FSData struct {
|
|
||||||
Path string
|
|
||||||
St *state.State
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlagManager handles centralized flag management
|
// FlagManager handles centralized flag management
|
||||||
type FlagManager struct {
|
type FlagManager struct {
|
||||||
parser *asm.FlagParser
|
parser *asm.FlagParser
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
"git.defalsify.org/vise.git/db"
|
"git.defalsify.org/vise.git/db"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
"git.defalsify.org/vise.git/state"
|
"git.defalsify.org/vise.git/state"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd/mocks"
|
"git.grassecon.net/urdt/ussd/internal/mocks"
|
||||||
"git.grassecon.net/urdt/ussd/internal/models"
|
"git.grassecon.net/urdt/ussd/internal/models"
|
||||||
"git.grassecon.net/urdt/ussd/internal/utils"
|
"git.grassecon.net/urdt/ussd/internal/utils"
|
||||||
"github.com/alecthomas/assert/v2"
|
"github.com/alecthomas/assert/v2"
|
||||||
|
@ -32,6 +32,7 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
|
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
|
||||||
ash.writeError(w, 400, err)
|
ash.writeError(w, 400, err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
rqs.Config = cfg
|
rqs.Config = cfg
|
||||||
rqs.Input, err = rp.GetInput(req)
|
rqs.Input, err = rp.GetInput(req)
|
||||||
@ -41,16 +42,14 @@ func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rqs, err = ash.Process(rqs)
|
rqs, err = ash.Process(rqs)
|
||||||
switch err {
|
switch err {
|
||||||
case handlers.ErrStorage:
|
case nil: // set code to 200 if no err
|
||||||
code = 500
|
code = 200
|
||||||
case handlers.ErrEngineInit:
|
case handlers.ErrStorage, handlers.ErrEngineInit, handlers.ErrEngineExec, handlers.ErrEngineType:
|
||||||
code = 500
|
|
||||||
case handlers.ErrEngineExec:
|
|
||||||
code = 500
|
code = 500
|
||||||
default:
|
default:
|
||||||
code = 200
|
code = 500
|
||||||
}
|
}
|
||||||
|
|
||||||
if code != 200 {
|
if code != 200 {
|
||||||
|
449
internal/http/http_test.go
Normal file
449
internal/http/http_test.go
Normal file
@ -0,0 +1,449 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/mocks/httpmocks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// invalidRequestType is a custom type to test invalid request scenarios
|
||||||
|
type invalidRequestType struct{}
|
||||||
|
|
||||||
|
// errorReader is a helper type that always returns an error when Read is called
|
||||||
|
type errorReader struct{}
|
||||||
|
|
||||||
|
func (e *errorReader) Read(p []byte) (n int, err error) {
|
||||||
|
return 0, errors.New("read error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewATSessionHandler(t *testing.T) {
|
||||||
|
mockHandler := &httpmocks.MockRequestHandler{}
|
||||||
|
ash := NewATSessionHandler(mockHandler)
|
||||||
|
|
||||||
|
if ash == nil {
|
||||||
|
t.Fatal("NewATSessionHandler returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ash.SessionHandler == nil {
|
||||||
|
t.Fatal("SessionHandler is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestATSessionHandler_ServeHTTP(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setupMocks func(*httpmocks.MockRequestHandler, *httpmocks.MockRequestParser, *httpmocks.MockEngine)
|
||||||
|
formData url.Values
|
||||||
|
expectedStatus int
|
||||||
|
expectedBody string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Successful request",
|
||||||
|
setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) {
|
||||||
|
mrp.GetSessionIdFunc = func(rq any) (string, error) {
|
||||||
|
req := rq.(*http.Request)
|
||||||
|
return req.FormValue("phoneNumber"), nil
|
||||||
|
}
|
||||||
|
mrp.GetInputFunc = func(rq any) ([]byte, error) {
|
||||||
|
req := rq.(*http.Request)
|
||||||
|
text := req.FormValue("text")
|
||||||
|
parts := strings.Split(text, "*")
|
||||||
|
return []byte(parts[len(parts)-1]), nil
|
||||||
|
}
|
||||||
|
mh.ProcessFunc = func(rqs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
rqs.Continue = true
|
||||||
|
rqs.Engine = me
|
||||||
|
return rqs, nil
|
||||||
|
}
|
||||||
|
mh.GetConfigFunc = func() engine.Config { return engine.Config{} }
|
||||||
|
mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp }
|
||||||
|
mh.OutputFunc = func(rs handlers.RequestSession) (handlers.RequestSession, error) { return rs, nil }
|
||||||
|
mh.ResetFunc = func(rs handlers.RequestSession) (handlers.RequestSession, error) { return rs, nil }
|
||||||
|
me.WriteResultFunc = func(context.Context, io.Writer) (int, error) { return 0, nil }
|
||||||
|
},
|
||||||
|
formData: url.Values{
|
||||||
|
"phoneNumber": []string{"+1234567890"},
|
||||||
|
"text": []string{"1*2*3"},
|
||||||
|
},
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
expectedBody: "CON ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GetSessionId error",
|
||||||
|
setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) {
|
||||||
|
mrp.GetSessionIdFunc = func(rq any) (string, error) {
|
||||||
|
return "", errors.New("no phone number found")
|
||||||
|
}
|
||||||
|
mh.GetConfigFunc = func() engine.Config { return engine.Config{} }
|
||||||
|
mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp }
|
||||||
|
},
|
||||||
|
formData: url.Values{
|
||||||
|
"text": []string{"1*2*3"},
|
||||||
|
},
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
expectedBody: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GetInput error",
|
||||||
|
setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) {
|
||||||
|
mrp.GetSessionIdFunc = func(rq any) (string, error) {
|
||||||
|
req := rq.(*http.Request)
|
||||||
|
return req.FormValue("phoneNumber"), nil
|
||||||
|
}
|
||||||
|
mrp.GetInputFunc = func(rq any) ([]byte, error) {
|
||||||
|
return nil, errors.New("no input found")
|
||||||
|
}
|
||||||
|
mh.GetConfigFunc = func() engine.Config { return engine.Config{} }
|
||||||
|
mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp }
|
||||||
|
},
|
||||||
|
formData: url.Values{
|
||||||
|
"phoneNumber": []string{"+1234567890"},
|
||||||
|
},
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
expectedBody: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Process error",
|
||||||
|
setupMocks: func(mh *httpmocks.MockRequestHandler, mrp *httpmocks.MockRequestParser, me *httpmocks.MockEngine) {
|
||||||
|
mrp.GetSessionIdFunc = func(rq any) (string, error) {
|
||||||
|
req := rq.(*http.Request)
|
||||||
|
return req.FormValue("phoneNumber"), nil
|
||||||
|
}
|
||||||
|
mrp.GetInputFunc = func(rq any) ([]byte, error) {
|
||||||
|
req := rq.(*http.Request)
|
||||||
|
text := req.FormValue("text")
|
||||||
|
parts := strings.Split(text, "*")
|
||||||
|
return []byte(parts[len(parts)-1]), nil
|
||||||
|
}
|
||||||
|
mh.ProcessFunc = func(rqs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return rqs, handlers.ErrStorage
|
||||||
|
}
|
||||||
|
mh.GetConfigFunc = func() engine.Config { return engine.Config{} }
|
||||||
|
mh.GetRequestParserFunc = func() handlers.RequestParser { return mrp }
|
||||||
|
},
|
||||||
|
formData: url.Values{
|
||||||
|
"phoneNumber": []string{"+1234567890"},
|
||||||
|
"text": []string{"1*2*3"},
|
||||||
|
},
|
||||||
|
expectedStatus: http.StatusInternalServerError,
|
||||||
|
expectedBody: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
mockHandler := &httpmocks.MockRequestHandler{}
|
||||||
|
mockRequestParser := &httpmocks.MockRequestParser{}
|
||||||
|
mockEngine := &httpmocks.MockEngine{}
|
||||||
|
tt.setupMocks(mockHandler, mockRequestParser, mockEngine)
|
||||||
|
|
||||||
|
ash := NewATSessionHandler(mockHandler)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tt.formData.Encode()))
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
ash.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
if w.Code != tt.expectedStatus {
|
||||||
|
t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.expectedBody != "" && w.Body.String() != tt.expectedBody {
|
||||||
|
t.Errorf("Expected body %q, got %q", tt.expectedBody, w.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestATSessionHandler_Output(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input handlers.RequestSession
|
||||||
|
expectedPrefix string
|
||||||
|
expectedError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Continue true",
|
||||||
|
input: handlers.RequestSession{
|
||||||
|
Continue: true,
|
||||||
|
Engine: &httpmocks.MockEngine{
|
||||||
|
WriteResultFunc: func(context.Context, io.Writer) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Writer: &httpmocks.MockWriter{},
|
||||||
|
},
|
||||||
|
expectedPrefix: "CON ",
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Continue false",
|
||||||
|
input: handlers.RequestSession{
|
||||||
|
Continue: false,
|
||||||
|
Engine: &httpmocks.MockEngine{
|
||||||
|
WriteResultFunc: func(context.Context, io.Writer) (int, error) {
|
||||||
|
return 0, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Writer: &httpmocks.MockWriter{},
|
||||||
|
},
|
||||||
|
expectedPrefix: "END ",
|
||||||
|
expectedError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "WriteResult error",
|
||||||
|
input: handlers.RequestSession{
|
||||||
|
Continue: true,
|
||||||
|
Engine: &httpmocks.MockEngine{
|
||||||
|
WriteResultFunc: func(context.Context, io.Writer) (int, error) {
|
||||||
|
return 0, errors.New("write error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Writer: &httpmocks.MockWriter{},
|
||||||
|
},
|
||||||
|
expectedPrefix: "CON ",
|
||||||
|
expectedError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ash := &ATSessionHandler{}
|
||||||
|
_, err := ash.Output(tt.input)
|
||||||
|
|
||||||
|
if tt.expectedError && err == nil {
|
||||||
|
t.Error("Expected an error, but got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.expectedError && err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mw := tt.input.Writer.(*httpmocks.MockWriter)
|
||||||
|
if !mw.WriteStringCalled {
|
||||||
|
t.Error("WriteString was not called")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mw.WrittenString != tt.expectedPrefix {
|
||||||
|
t.Errorf("Expected prefix %q, got %q", tt.expectedPrefix, mw.WrittenString)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionHandler_ServeHTTP(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
sessionID string
|
||||||
|
input []byte
|
||||||
|
parserErr error
|
||||||
|
processErr error
|
||||||
|
outputErr error
|
||||||
|
resetErr error
|
||||||
|
expectedStatus int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Success",
|
||||||
|
sessionID: "123",
|
||||||
|
input: []byte("test input"),
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Missing Session ID",
|
||||||
|
sessionID: "",
|
||||||
|
parserErr: handlers.ErrSessionMissing,
|
||||||
|
expectedStatus: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Process Error",
|
||||||
|
sessionID: "123",
|
||||||
|
input: []byte("test input"),
|
||||||
|
processErr: handlers.ErrStorage,
|
||||||
|
expectedStatus: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Output Error",
|
||||||
|
sessionID: "123",
|
||||||
|
input: []byte("test input"),
|
||||||
|
outputErr: errors.New("output error"),
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Reset Error",
|
||||||
|
sessionID: "123",
|
||||||
|
input: []byte("test input"),
|
||||||
|
resetErr: errors.New("reset error"),
|
||||||
|
expectedStatus: http.StatusOK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
mockRequestParser := &httpmocks.MockRequestParser{
|
||||||
|
GetSessionIdFunc: func(any) (string, error) {
|
||||||
|
return tt.sessionID, tt.parserErr
|
||||||
|
},
|
||||||
|
GetInputFunc: func(any) ([]byte, error) {
|
||||||
|
return tt.input, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockRequestHandler := &httpmocks.MockRequestHandler{
|
||||||
|
ProcessFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return rs, tt.processErr
|
||||||
|
},
|
||||||
|
OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return rs, tt.outputErr
|
||||||
|
},
|
||||||
|
ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return rs, tt.resetErr
|
||||||
|
},
|
||||||
|
GetRequestParserFunc: func() handlers.RequestParser {
|
||||||
|
return mockRequestParser
|
||||||
|
},
|
||||||
|
GetConfigFunc: func() engine.Config {
|
||||||
|
return engine.Config{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionHandler := ToSessionHandler(mockRequestHandler)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(tt.input))
|
||||||
|
req.Header.Set("X-Vise-Session", tt.sessionID)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
sessionHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
if status := rr.Code; status != tt.expectedStatus {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, tt.expectedStatus)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionHandler_writeError(t *testing.T) {
|
||||||
|
handler := &SessionHandler{}
|
||||||
|
mockWriter := &httpmocks.MockWriter{}
|
||||||
|
err := errors.New("test error")
|
||||||
|
|
||||||
|
handler.writeError(mockWriter, http.StatusBadRequest, err)
|
||||||
|
|
||||||
|
if mockWriter.WrittenString != "" {
|
||||||
|
t.Errorf("Expected empty body, got %s", mockWriter.WrittenString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultRequestParser_GetSessionId(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
request any
|
||||||
|
expectedID string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid Session ID",
|
||||||
|
request: func() *http.Request {
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||||
|
req.Header.Set("X-Vise-Session", "123456")
|
||||||
|
return req
|
||||||
|
}(),
|
||||||
|
expectedID: "123456",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Missing Session ID",
|
||||||
|
request: httptest.NewRequest(http.MethodPost, "/", nil),
|
||||||
|
expectedID: "",
|
||||||
|
expectedError: handlers.ErrSessionMissing,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid Request Type",
|
||||||
|
request: invalidRequestType{},
|
||||||
|
expectedID: "",
|
||||||
|
expectedError: handlers.ErrInvalidRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := &DefaultRequestParser{}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
id, err := parser.GetSessionId(tt.request)
|
||||||
|
|
||||||
|
if id != tt.expectedID {
|
||||||
|
t.Errorf("Expected session ID %s, got %s", tt.expectedID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != tt.expectedError {
|
||||||
|
t.Errorf("Expected error %v, got %v", tt.expectedError, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultRequestParser_GetInput(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
request any
|
||||||
|
expectedInput []byte
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Valid Input",
|
||||||
|
request: func() *http.Request {
|
||||||
|
return httptest.NewRequest(http.MethodPost, "/", bytes.NewBufferString("test input"))
|
||||||
|
}(),
|
||||||
|
expectedInput: []byte("test input"),
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Input",
|
||||||
|
request: httptest.NewRequest(http.MethodPost, "/", nil),
|
||||||
|
expectedInput: []byte{},
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Invalid Request Type",
|
||||||
|
request: invalidRequestType{},
|
||||||
|
expectedInput: nil,
|
||||||
|
expectedError: handlers.ErrInvalidRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Read Error",
|
||||||
|
request: func() *http.Request {
|
||||||
|
return httptest.NewRequest(http.MethodPost, "/", &errorReader{})
|
||||||
|
}(),
|
||||||
|
expectedInput: nil,
|
||||||
|
expectedError: errors.New("read error"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := &DefaultRequestParser{}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
input, err := parser.GetInput(tt.request)
|
||||||
|
|
||||||
|
if !bytes.Equal(input, tt.expectedInput) {
|
||||||
|
t.Errorf("Expected input %s, got %s", tt.expectedInput, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != tt.expectedError && (err == nil || err.Error() != tt.expectedError.Error()) {
|
||||||
|
t.Errorf("Expected error %v, got %v", tt.expectedError, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
30
internal/mocks/httpmocks/enginemock.go
Normal file
30
internal/mocks/httpmocks/enginemock.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package httpmocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockEngine implements the engine.Engine interface for testing
|
||||||
|
type MockEngine struct {
|
||||||
|
InitFunc func(context.Context) (bool, error)
|
||||||
|
ExecFunc func(context.Context, []byte) (bool, error)
|
||||||
|
WriteResultFunc func(context.Context, io.Writer) (int, error)
|
||||||
|
FinishFunc func() error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockEngine) Init(ctx context.Context) (bool, error) {
|
||||||
|
return m.InitFunc(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockEngine) Exec(ctx context.Context, input []byte) (bool, error) {
|
||||||
|
return m.ExecFunc(ctx, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockEngine) WriteResult(ctx context.Context, w io.Writer) (int, error) {
|
||||||
|
return m.WriteResultFunc(ctx, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockEngine) Finish() error {
|
||||||
|
return m.FinishFunc()
|
||||||
|
}
|
47
internal/mocks/httpmocks/requesthandlermock.go
Normal file
47
internal/mocks/httpmocks/requesthandlermock.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package httpmocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.defalsify.org/vise.git/persist"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockRequestHandler implements handlers.RequestHandler interface for testing
|
||||||
|
type MockRequestHandler struct {
|
||||||
|
ProcessFunc func(handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
|
GetConfigFunc func() engine.Config
|
||||||
|
GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
|
||||||
|
OutputFunc func(rs handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
|
ResetFunc func(rs handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
|
ShutdownFunc func()
|
||||||
|
GetRequestParserFunc func() handlers.RequestParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return m.ProcessFunc(rqs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) GetConfig() engine.Config {
|
||||||
|
return m.GetConfigFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine {
|
||||||
|
return m.GetEngineFunc(cfg, rs, pe)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Output(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return m.OutputFunc(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Reset(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
|
return m.ResetFunc(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Shutdown() {
|
||||||
|
m.ShutdownFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) GetRequestParser() handlers.RequestParser {
|
||||||
|
return m.GetRequestParserFunc()
|
||||||
|
}
|
15
internal/mocks/httpmocks/requestparsermock.go
Normal file
15
internal/mocks/httpmocks/requestparsermock.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package httpmocks
|
||||||
|
|
||||||
|
// MockRequestParser implements the handlers.RequestParser interface for testing
|
||||||
|
type MockRequestParser struct {
|
||||||
|
GetSessionIdFunc func(any) (string, error)
|
||||||
|
GetInputFunc func(any) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestParser) GetSessionId(rq any) (string, error) {
|
||||||
|
return m.GetSessionIdFunc(rq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestParser) GetInput(rq any) ([]byte, error) {
|
||||||
|
return m.GetInputFunc(rq)
|
||||||
|
}
|
25
internal/mocks/httpmocks/writermock.go
Normal file
25
internal/mocks/httpmocks/writermock.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package httpmocks
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// MockWriter implements a mock io.Writer for testing
|
||||||
|
type MockWriter struct {
|
||||||
|
WriteStringCalled bool
|
||||||
|
WrittenString string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWriter) Write(p []byte) (n int, err error) {
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWriter) WriteString(s string) (n int, err error) {
|
||||||
|
m.WriteStringCalled = true
|
||||||
|
m.WrittenString = s
|
||||||
|
return len(s), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWriter) Header() http.Header {
|
||||||
|
return http.Header{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockWriter) WriteHeader(statusCode int) {}
|
64
internal/storage/storageservice.go
Normal file
64
internal/storage/storageservice.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/db"
|
||||||
|
fsdb "git.defalsify.org/vise.git/db/fs"
|
||||||
|
gdbmdb "git.defalsify.org/vise.git/db/gdbm"
|
||||||
|
"git.defalsify.org/vise.git/persist"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StorageService interface {
|
||||||
|
GetPersister(dbDir string, ctx context.Context) (*persist.Persister, error)
|
||||||
|
GetUserdataDb(dbDir string, ctx context.Context) db.Db
|
||||||
|
GetResource(resourceDir string, ctx context.Context) (resource.Resource, error)
|
||||||
|
EnsureDbDir(dbDir string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuStorageService struct{}
|
||||||
|
|
||||||
|
func (menuStorageService *MenuStorageService) GetPersister(dbDir string, ctx context.Context) (*persist.Persister, error) {
|
||||||
|
store := gdbmdb.NewGdbmDb()
|
||||||
|
storeFile := path.Join(dbDir, "state.gdbm")
|
||||||
|
store.Connect(ctx, storeFile)
|
||||||
|
pr := persist.NewPersister(store)
|
||||||
|
return pr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (menuStorageService *MenuStorageService) GetUserdataDb(dbDir string, ctx context.Context) db.Db {
|
||||||
|
store := gdbmdb.NewGdbmDb()
|
||||||
|
storeFile := path.Join(dbDir, "userdata.gdbm")
|
||||||
|
store.Connect(ctx, storeFile)
|
||||||
|
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (menuStorageService *MenuStorageService) GetResource(resourceDir string, ctx context.Context) (resource.Resource, error) {
|
||||||
|
store := fsdb.NewFsDb()
|
||||||
|
err := store.Connect(ctx, resourceDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rfs := resource.NewDbResource(store)
|
||||||
|
return rfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (menuStorageService *MenuStorageService) GetStateStore(dbDir string, ctx context.Context) (db.Db, error) {
|
||||||
|
store := gdbmdb.NewGdbmDb()
|
||||||
|
storeFile := path.Join(dbDir, "state.gdbm")
|
||||||
|
store.Connect(ctx, storeFile)
|
||||||
|
return store, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (menuStorageService *MenuStorageService) EnsureDbDir(dbDir string) error {
|
||||||
|
err := os.MkdirAll(dbDir, 0700)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user