Compare commits
54 Commits
v0.8.0-bet
...
e09e324a50
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e09e324a50 | ||
|
|
599815c343
|
||
|
|
462c0d7677 | ||
| 80b96e9bf6 | |||
| b5561decd1 | |||
|
|
02823fd64e | ||
|
|
cd575c2edb | ||
| f3d4f35718 | |||
| 52787bdb4d | |||
|
|
f8d8f265f1
|
||
|
|
9371b52f3e | ||
|
|
563000ec15 | ||
|
824d39908b
|
|||
|
|
52fd1eced2
|
||
|
a312ea5b84
|
|||
|
|
5c7a535288 | ||
|
|
cc2f7b41df | ||
|
|
d39740a09a
|
||
|
|
bb4037e73f
|
||
|
|
739fd90dfd
|
||
|
|
6789c4f550
|
||
|
|
437f73827d
|
||
|
|
f0a4a0df61
|
||
|
|
bd604219b8
|
||
|
|
51b6fc0dde
|
||
|
|
cc9760125a
|
||
|
|
3a9f3fa373
|
||
|
|
89c21847b9
|
||
|
|
450dfa02cc
|
||
|
|
f61e65f4fe
|
||
|
|
a4d6cef9c0
|
||
|
|
2992f7ae8e
|
||
|
|
dc61d05584
|
||
|
|
83857026d3 | ||
|
|
349051b5ef | ||
|
|
e92e498726 | ||
|
|
c3cbe1cd92
|
||
|
|
418080d093 | ||
|
|
2e30739ec9
|
||
|
|
dc1674ec55
|
||
|
|
daec816a3e
|
||
|
|
ac0c43cb43
|
||
|
|
9013cc3618
|
||
|
|
056d056613
|
||
|
|
e581ec4771 | ||
|
|
e16b7445e8
|
||
|
|
1b12f0ba5f
|
||
|
|
c1e0617bb3
|
||
|
|
6723884103
|
||
|
|
b888af446d
|
||
|
|
43b2c3b78d
|
||
|
|
d67853f6d9 | ||
|
|
06230dc557
|
||
|
|
6ee2c88fe2
|
@@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
!/cmd/africastalking
|
!/cmd/africastalking
|
||||||
|
!/cmd/ssh
|
||||||
!/common
|
!/common
|
||||||
!/config
|
!/config
|
||||||
!/initializers
|
!/initializers
|
||||||
|
|||||||
14
.env.example
14
.env.example
@@ -6,15 +6,15 @@ HOST=127.0.0.1
|
|||||||
AT_ENDPOINT=/ussd/africastalking
|
AT_ENDPOINT=/ussd/africastalking
|
||||||
|
|
||||||
#PostgreSQL
|
#PostgreSQL
|
||||||
DB_HOST=localhost
|
DB_CONN=postgres://postgres:strongpass@localhost:5432/urdt_ussd
|
||||||
DB_USER=postgres
|
#DB_TIMEZONE=Africa/Nairobi
|
||||||
DB_PASSWORD=strongpass
|
#DB_SCHEMA=vise
|
||||||
DB_NAME=urdt_ussd
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_SSLMODE=disable
|
|
||||||
DB_TIMEZONE=Africa/Nairobi
|
|
||||||
|
|
||||||
#External API Calls
|
#External API Calls
|
||||||
CUSTODIAL_URL_BASE=http://localhost:5003
|
CUSTODIAL_URL_BASE=http://localhost:5003
|
||||||
BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr
|
BEARER_TOKEN=eyJeSIsInRcCI6IkpXVCJ.yJwdWJsaWNLZXkiOiIwrrrrrr
|
||||||
DATA_URL_BASE=http://localhost:5006
|
DATA_URL_BASE=http://localhost:5006
|
||||||
|
|
||||||
|
#Language
|
||||||
|
DEFAULT_LANGUAGE=eng
|
||||||
|
LANGUAGES=eng, swa
|
||||||
|
|||||||
@@ -39,5 +39,6 @@ COPY --from=build /build/.env.example .
|
|||||||
RUN mv .env.example .env
|
RUN mv .env.example .env
|
||||||
|
|
||||||
EXPOSE 7123
|
EXPOSE 7123
|
||||||
|
EXPOSE 7122
|
||||||
|
|
||||||
CMD ["./ussd-africastalking"]
|
CMD ["./ussd-africastalking"]
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path"
|
|
||||||
"strconv"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
|
||||||
"git.defalsify.org/vise.git/resource"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/config"
|
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/http/at"
|
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http/at"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
logg = logging.NewVanilla().WithDomain("AfricasTalking").WithContextKey("at-session-id")
|
|
||||||
scriptDir = path.Join("services", "registration")
|
|
||||||
build = "dev"
|
|
||||||
menuSeparator = ": "
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initializers.LoadEnvVariables()
|
|
||||||
}
|
|
||||||
func main() {
|
|
||||||
config.LoadConfig()
|
|
||||||
|
|
||||||
var dbDir string
|
|
||||||
var resourceDir string
|
|
||||||
var size uint
|
|
||||||
var database string
|
|
||||||
var engineDebug bool
|
|
||||||
var host string
|
|
||||||
var port uint
|
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
|
||||||
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
|
|
||||||
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
logg.Infof("start command", "build", build, "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
|
||||||
Root: "root",
|
|
||||||
OutputSize: uint32(size),
|
|
||||||
FlagCount: uint32(128),
|
|
||||||
MenuSeparator: menuSeparator,
|
|
||||||
}
|
|
||||||
|
|
||||||
if engineDebug {
|
|
||||||
cfg.EngineDebug = true
|
|
||||||
}
|
|
||||||
|
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer userdataStore.Close()
|
|
||||||
|
|
||||||
dbResource, ok := rs.(*resource.DbResource)
|
|
||||||
if !ok {
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(ctx, pfp, true, dbResource, cfg, rs)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
lhs.SetDataStore(&userdataStore)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
accountService := remote.AccountService{}
|
|
||||||
hl, err := lhs.GetHandler(&accountService)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
stateStore, err := menuStorageService.GetStateStore(ctx)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer stateStore.Close()
|
|
||||||
|
|
||||||
rp := &at.ATRequestParser{}
|
|
||||||
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
|
||||||
sh := httpserver.NewATSessionHandler(bsh)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
mux.Handle(initializers.GetEnv("AT_ENDPOINT", "/"), sh)
|
|
||||||
|
|
||||||
s := &http.Server{
|
|
||||||
Addr: fmt.Sprintf("%s:%s", host, strconv.Itoa(int(port))),
|
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
s.RegisterOnShutdown(sh.Shutdown)
|
|
||||||
|
|
||||||
cint := make(chan os.Signal)
|
|
||||||
cterm := make(chan os.Signal)
|
|
||||||
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
|
|
||||||
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case _ = <-cint:
|
|
||||||
case _ = <-cterm:
|
|
||||||
}
|
|
||||||
s.Shutdown(ctx)
|
|
||||||
}()
|
|
||||||
err = s.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
logg.Infof("Server closed with error", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,12 +12,15 @@ import (
|
|||||||
"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.defalsify.org/vise.git/lang"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/config"
|
"git.grassecon.net/urdt/ussd/config"
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
|
"git.grassecon.net/urdt/ussd/request"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/args"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -46,28 +49,50 @@ func (p *asyncRequestParser) GetInput(r any) ([]byte, error) {
|
|||||||
func main() {
|
func main() {
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
|
var connStr string
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var dbDir string
|
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
var database string
|
var database string
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
|
var err error
|
||||||
|
var gettextDir string
|
||||||
|
var langs args.LangVar
|
||||||
|
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
|
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
|
||||||
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
||||||
|
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
|
||||||
|
flag.Var(&langs, "language", "add symbol resolution for language")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logg.Infof("start command", "conn", connData, "resourcedir", resourceDir, "outputsize", size, "sessionId", sessionId)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
|
|
||||||
|
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, "Language", ln)
|
||||||
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
@@ -81,18 +106,18 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
rs, err := menuStorageService.GetResource(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 = menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
|
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -128,7 +153,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 := request.RequestSession{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Writer: os.Stdout,
|
Writer: os.Stdout,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ import (
|
|||||||
"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.defalsify.org/vise.git/lang"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/config"
|
"git.grassecon.net/urdt/ussd/config"
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/handlers"
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/args"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -36,26 +38,48 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
var dbDir string
|
var connStr string
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
var database string
|
var database string
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
var err error
|
||||||
|
var gettextDir string
|
||||||
|
var langs args.LangVar
|
||||||
|
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
|
flag.StringVar(&host, "h", initializers.GetEnv("HOST", "127.0.0.1"), "http host")
|
||||||
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
flag.UintVar(&port, "p", initializers.GetEnvUint("PORT", 7123), "http port")
|
||||||
|
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
|
||||||
|
flag.Var(&langs, "language", "add symbol resolution for language")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size)
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logg.Infof("start command", "conn", connData, "resourcedir", resourceDir, "outputsize", size)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
|
|
||||||
|
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, "Language", ln)
|
||||||
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
@@ -69,19 +93,14 @@ func main() {
|
|||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
rs, err := menuStorageService.GetResource(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 = menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
|
userdataStore, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
@@ -118,7 +137,11 @@ func main() {
|
|||||||
|
|
||||||
rp := &httpserver.DefaultRequestParser{}
|
rp := &httpserver.DefaultRequestParser{}
|
||||||
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
||||||
sh := httpserver.ToSessionHandler(bsh)
|
// TODO: less hacky way of making session handler
|
||||||
|
//sh := request.ToSessionHandler(bsh)
|
||||||
|
sh := &httpserver.SessionHandler{
|
||||||
|
RequestHandler: 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,
|
||||||
|
|||||||
52
cmd/main.go
52
cmd/main.go
@@ -10,10 +10,12 @@ import (
|
|||||||
"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.defalsify.org/vise.git/lang"
|
||||||
"git.grassecon.net/urdt/ussd/config"
|
"git.grassecon.net/urdt/ussd/config"
|
||||||
|
"git.grassecon.net/urdt/ussd/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/args"
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,26 +29,55 @@ func init() {
|
|||||||
initializers.LoadEnvVariables()
|
initializers.LoadEnvVariables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: external script automatically generate language handler list from select language vise code OR consider dynamic menu generation script possibility
|
||||||
func main() {
|
func main() {
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
var dbDir string
|
var connStr string
|
||||||
var size uint
|
var size uint
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var database string
|
var database string
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
|
var resourceDir string
|
||||||
|
var err error
|
||||||
|
var gettextDir string
|
||||||
|
var langs args.LangVar
|
||||||
|
|
||||||
|
flag.StringVar(&resourceDir, "resourcedir", scriptDir, "resource dir")
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
|
flag.StringVar(&gettextDir, "gettext", "", "use gettext translations from given directory")
|
||||||
|
flag.Var(&langs, "language", "add symbol resolution for language")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "outputsize", size)
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logg.Infof("start command", "conn", connData, "outputsize", size)
|
||||||
|
|
||||||
|
if len(langs.Langs()) == 0 {
|
||||||
|
langs.Set(config.DefaultLanguage)
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
|
|
||||||
|
ln, err := lang.LanguageFromCode(config.DefaultLanguage)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "default language set error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, "Language", ln)
|
||||||
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
cfg := engine.Config{
|
cfg := engine.Config{
|
||||||
@@ -57,13 +88,10 @@ func main() {
|
|||||||
MenuSeparator: menuSeparator,
|
MenuSeparator: menuSeparator,
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceDir := scriptDir
|
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
|
||||||
|
if gettextDir != "" {
|
||||||
err := menuStorageService.EnsureDbDir()
|
menuStorageService = menuStorageService.WithGettext(gettextDir, langs.Langs())
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
rs, err := menuStorageService.GetResource(ctx)
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
@@ -14,17 +14,29 @@ import (
|
|||||||
"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.grassecon.net/urdt/ussd/config"
|
||||||
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/ssh"
|
"git.grassecon.net/urdt/ussd/internal/ssh"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
keyStore db.Db
|
keyStore db.Db
|
||||||
logg = logging.NewVanilla()
|
logg = logging.NewVanilla()
|
||||||
scriptDir = path.Join("services", "registration")
|
scriptDir = path.Join("services", "registration")
|
||||||
|
|
||||||
|
build = "dev"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initializers.LoadEnvVariables()
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
config.LoadConfig()
|
||||||
|
|
||||||
|
var connStr string
|
||||||
var dbDir string
|
var dbDir string
|
||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
@@ -32,17 +44,25 @@ func main() {
|
|||||||
var stateDebug bool
|
var stateDebug bool
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
flag.StringVar(&host, "h", "127.0.0.1", "socket host")
|
||||||
flag.UintVar(&port, "p", 7122, "http port")
|
flag.UintVar(&port, "p", 7122, "socket port")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
sshKeyFile := flag.Arg(0)
|
sshKeyFile := flag.Arg(0)
|
||||||
_, err := os.Stat(sshKeyFile)
|
_, err = os.Stat(sshKeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "cannot open ssh server private key file: %v\n", err)
|
fmt.Fprintf(os.Stderr, "cannot open ssh server private key file: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -76,7 +96,7 @@ func main() {
|
|||||||
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer func () {
|
defer func() {
|
||||||
logg.TraceCtxf(ctx, "shutdown auth key store reached")
|
logg.TraceCtxf(ctx, "shutdown auth key store reached")
|
||||||
err = authKeyStore.Close()
|
err = authKeyStore.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -93,11 +113,11 @@ func main() {
|
|||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Debug: engineDebug,
|
Debug: engineDebug,
|
||||||
FlagFile: pfp,
|
FlagFile: pfp,
|
||||||
DbDir: dbDir,
|
Conn: connData,
|
||||||
ResourceDir: resourceDir,
|
ResourceDir: resourceDir,
|
||||||
SrvKeyFile: sshKeyFile,
|
SrvKeyFile: sshKeyFile,
|
||||||
Host: host,
|
Host: host,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
@@ -109,7 +129,7 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logg.ErrorCtxf(ctx, "runner stop error", "err", err)
|
logg.ErrorCtxf(ctx, "runner stop error", "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
runner.Run(ctx, authKeyStore)
|
runner.Run(ctx, authKeyStore)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ import (
|
|||||||
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
|
dbstorage "git.grassecon.net/urdt/ussd/internal/storage/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ToConnData = storage.ToConnData
|
||||||
|
)
|
||||||
|
|
||||||
func StoreToDb(store *UserDataStore) db.Db {
|
func StoreToDb(store *UserDataStore) db.Db {
|
||||||
return store.Db
|
return store.Db
|
||||||
}
|
}
|
||||||
@@ -23,17 +27,23 @@ type StorageServices interface {
|
|||||||
GetPersister(ctx context.Context) (*persist.Persister, error)
|
GetPersister(ctx context.Context) (*persist.Persister, error)
|
||||||
GetUserdataDb(ctx context.Context) (db.Db, error)
|
GetUserdataDb(ctx context.Context) (db.Db, error)
|
||||||
GetResource(ctx context.Context) (resource.Resource, error)
|
GetResource(ctx context.Context) (resource.Resource, error)
|
||||||
EnsureDbDir() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageService struct {
|
type StorageService struct {
|
||||||
svc *storage.MenuStorageService
|
svc *storage.MenuStorageService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorageService(dbDir string) *StorageService {
|
func NewStorageService(conn storage.ConnData) (*StorageService, error) {
|
||||||
return &StorageService{
|
svc := &StorageService{
|
||||||
svc: storage.NewMenuStorageService(dbDir, ""),
|
svc: storage.NewMenuStorageService(conn, ""),
|
||||||
}
|
}
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: simplify enable poresource, conndata instead
|
||||||
|
func(ss *StorageService) SetResourceDir(resourceDir string) error {
|
||||||
|
ss.svc = ss.svc.WithResourceDir(resourceDir)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
|
func(ss *StorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
|
||||||
@@ -48,6 +58,6 @@ func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, er
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func(ss *StorageService) EnsureDbDir() error {
|
func(ss *StorageService) GetStateStore(ctx context.Context) (db.Db, error) {
|
||||||
return ss.svc.EnsureDbDir()
|
return ss.svc.GetStateStore(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
)
|
)
|
||||||
@@ -18,6 +19,11 @@ const (
|
|||||||
AliasPrefix = "api/v1/alias"
|
AliasPrefix = "api/v1/alias"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultLanguage = "eng"
|
||||||
|
languages []string
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
custodialURLBase string
|
custodialURLBase string
|
||||||
dataURLBase string
|
dataURLBase string
|
||||||
@@ -34,8 +40,29 @@ var (
|
|||||||
VoucherTransfersURL string
|
VoucherTransfersURL string
|
||||||
VoucherDataURL string
|
VoucherDataURL string
|
||||||
CheckAliasURL string
|
CheckAliasURL string
|
||||||
|
DbConn string
|
||||||
|
DefaultLanguage string
|
||||||
|
Languages []string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func setLanguage() error {
|
||||||
|
defaultLanguage = initializers.GetEnv("DEFAULT_LANGUAGE", defaultLanguage)
|
||||||
|
languages = strings.Split(initializers.GetEnv("LANGUAGES", defaultLanguage), ",")
|
||||||
|
haveDefaultLanguage := false
|
||||||
|
for i, v := range(languages) {
|
||||||
|
languages[i] = strings.ReplaceAll(v, " ", "")
|
||||||
|
if languages[i] == defaultLanguage {
|
||||||
|
haveDefaultLanguage = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveDefaultLanguage {
|
||||||
|
languages = append([]string{defaultLanguage}, languages...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func setBase() error {
|
func setBase() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -43,14 +70,20 @@ func setBase() error {
|
|||||||
dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006")
|
dataURLBase = initializers.GetEnv("DATA_URL_BASE", "http://localhost:5006")
|
||||||
BearerToken = initializers.GetEnv("BEARER_TOKEN", "")
|
BearerToken = initializers.GetEnv("BEARER_TOKEN", "")
|
||||||
|
|
||||||
_, err = url.JoinPath(custodialURLBase, "/foo")
|
_, err = url.Parse(custodialURLBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = url.JoinPath(dataURLBase, "/bar")
|
_, err = url.Parse(dataURLBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setConn() error {
|
||||||
|
DbConn = initializers.GetEnv("DB_CONN", "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +93,14 @@ func LoadConfig() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = setConn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = setLanguage()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath)
|
CreateAccountURL, _ = url.JoinPath(custodialURLBase, createAccountPath)
|
||||||
TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath)
|
TrackStatusURL, _ = url.JoinPath(custodialURLBase, trackStatusPath)
|
||||||
BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix)
|
BalanceURL, _ = url.JoinPath(custodialURLBase, balancePathPrefix)
|
||||||
@@ -69,6 +110,8 @@ func LoadConfig() error {
|
|||||||
VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix)
|
VoucherTransfersURL, _ = url.JoinPath(dataURLBase, voucherTransfersPathPrefix)
|
||||||
VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix)
|
VoucherDataURL, _ = url.JoinPath(dataURLBase, voucherDataPathPrefix)
|
||||||
CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix)
|
CheckAliasURL, _ = url.JoinPath(dataURLBase, AliasPrefix)
|
||||||
|
DefaultLanguage = defaultLanguage
|
||||||
|
Languages = languages
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
126
devtools/lang/main.go
Normal file
126
devtools/lang/main.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// create language files from environment
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.defalsify.org/vise.git/lang"
|
||||||
|
"git.grassecon.net/urdt/ussd/config"
|
||||||
|
"git.grassecon.net/urdt/ussd/initializers"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
|
||||||
|
changeHeadSrc = `LOAD reset_account_authorized 0
|
||||||
|
LOAD reset_incorrect 0
|
||||||
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
|
CATCH pin_entry flag_account_authorized 0
|
||||||
|
`
|
||||||
|
|
||||||
|
selectSrc = `LOAD set_language 6
|
||||||
|
RELOAD set_language
|
||||||
|
CATCH terms flag_account_created 0
|
||||||
|
MOVE language_changed
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla()
|
||||||
|
mouts string
|
||||||
|
incmps string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
initializers.LoadEnvVariables()
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLanguageLabel(ln lang.Language) string {
|
||||||
|
s := ln.Name
|
||||||
|
v := strings.Split(s, " (")
|
||||||
|
if len(v) > 1 {
|
||||||
|
s = v[0]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLanguageKey(ln lang.Language) string {
|
||||||
|
s := toLanguageLabel(ln)
|
||||||
|
return strings.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var srcDir string
|
||||||
|
|
||||||
|
flag.StringVar(&srcDir, "o", ".", "resource dir write to")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
logg.Infof("start command", "dir", srcDir)
|
||||||
|
|
||||||
|
err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "config load error: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
logg.Tracef("using languages", "lang", config.Languages)
|
||||||
|
|
||||||
|
for i, v := range(config.Languages) {
|
||||||
|
ln, err := lang.LanguageFromCode(v)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error parsing language: %s\n", v)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
n := i + 1
|
||||||
|
s := toLanguageKey(ln)
|
||||||
|
mouts += fmt.Sprintf("MOUT %s %v\n", s, n)
|
||||||
|
v = "set_" + ln.Code
|
||||||
|
incmps += fmt.Sprintf("INCMP %s %v\n", v, n)
|
||||||
|
|
||||||
|
p := path.Join(srcDir, v)
|
||||||
|
w, err := os.OpenFile(p, os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0600)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed open language set template output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
s = toLanguageLabel(ln)
|
||||||
|
defer w.Close()
|
||||||
|
_, err = w.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed write select language vis output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src := mouts + "HALT\n" + incmps
|
||||||
|
src += "INCMP . *\n"
|
||||||
|
|
||||||
|
p := path.Join(srcDir, "select_language.vis")
|
||||||
|
w, err := os.OpenFile(p, os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0600)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed open select language vis output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
_, err = w.Write([]byte(src))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed write select language vis output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
src = changeHeadSrc + src
|
||||||
|
p = path.Join(srcDir, "change_language.vis")
|
||||||
|
w, err = os.OpenFile(p, os.O_WRONLY | os.O_CREATE | os.O_EXCL, 0600)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed open select language vis output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
_, err = w.Write([]byte(src))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed write select language vis output: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,26 +25,46 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func formatItem(k []byte, v []byte) (string, error) {
|
||||||
|
o, err := debug.FromKey(k)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s := fmt.Sprintf("%vValue: %v\n\n", o, string(v))
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
var dbDir string
|
var connStr string
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var database string
|
var database string
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
|
var err error
|
||||||
|
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
flag.StringVar(&connStr, "c", ".state", "connection string")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logg.Infof("start command", "conn", connData)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
|
|
||||||
resourceDir := scriptDir
|
resourceDir := scriptDir
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
|
||||||
|
|
||||||
store, err := menuStorageService.GetUserdataDb(ctx)
|
store, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -64,12 +84,12 @@ func main() {
|
|||||||
if k == nil {
|
if k == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
o, err := debug.FromKey(k)
|
r, err := formatItem(k, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "format db item error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Printf("%vValue: %v\n\n", o, string(v))
|
fmt.Printf(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.Close()
|
err = store.Close()
|
||||||
@@ -28,24 +28,35 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
var dbDir string
|
var connStr string
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var database string
|
var database string
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
|
var err error
|
||||||
|
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&database, "db", "gdbm", "database to be used")
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if connStr != "" {
|
||||||
|
connStr = config.DbConn
|
||||||
|
}
|
||||||
|
connData, err := storage.ToConnData(config.DbConn)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
logg.Infof("start command", "conn", connData)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "Database", database)
|
ctx = context.WithValue(ctx, "Database", database)
|
||||||
|
|
||||||
resourceDir := scriptDir
|
resourceDir := scriptDir
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
menuStorageService := storage.NewMenuStorageService(connData, resourceDir)
|
||||||
|
|
||||||
store, err := menuStorageService.GetUserdataDb(ctx)
|
store, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
15
errors/errors.go
Normal file
15
errors/errors.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidRequest = handlers.ErrInvalidRequest
|
||||||
|
ErrSessionMissing = handlers.ErrSessionMissing
|
||||||
|
ErrInvalidInput = handlers.ErrInvalidInput
|
||||||
|
ErrStorage = handlers.ErrStorage
|
||||||
|
ErrEngineType = handlers.ErrEngineType
|
||||||
|
ErrEngineInit = handlers.ErrEngineInit
|
||||||
|
ErrEngineExec = handlers.ErrEngineExec
|
||||||
|
)
|
||||||
@@ -5,20 +5,27 @@ import (
|
|||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.defalsify.org/vise.git/persist"
|
"git.defalsify.org/vise.git/persist"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/request"
|
||||||
|
"git.grassecon.net/urdt/ussd/errors"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
"git.grassecon.net/urdt/ussd/internal/handlers/ussd"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("handlers")
|
||||||
|
)
|
||||||
|
|
||||||
type BaseSessionHandler struct {
|
type BaseSessionHandler struct {
|
||||||
cfgTemplate engine.Config
|
cfgTemplate engine.Config
|
||||||
rp RequestParser
|
rp request.RequestParser
|
||||||
rs resource.Resource
|
rs resource.Resource
|
||||||
hn *ussd.Handlers
|
hn *ussd.Handlers
|
||||||
provider storage.StorageProvider
|
provider storage.StorageProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *ussd.Handlers) *BaseSessionHandler {
|
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp request.RequestParser, hn *ussd.Handlers) *BaseSessionHandler {
|
||||||
return &BaseSessionHandler{
|
return &BaseSessionHandler{
|
||||||
cfgTemplate: cfg,
|
cfgTemplate: cfg,
|
||||||
rs: rs,
|
rs: rs,
|
||||||
@@ -41,7 +48,7 @@ func(f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, p
|
|||||||
return en
|
return en
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error) {
|
func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.RequestSession, error) {
|
||||||
var r bool
|
var r bool
|
||||||
var err error
|
var err error
|
||||||
var ok bool
|
var ok bool
|
||||||
@@ -51,7 +58,7 @@ func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error)
|
|||||||
rqs.Storage, err = f.provider.Get(rqs.Config.SessionId)
|
rqs.Storage, err = f.provider.Get(rqs.Config.SessionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "storage get error", err)
|
logg.ErrorCtxf(rqs.Ctx, "", "storage get error", err)
|
||||||
return rqs, ErrStorage
|
return rqs, errors.ErrStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
f.hn = f.hn.WithPersister(rqs.Storage.Persister)
|
f.hn = f.hn.WithPersister(rqs.Storage.Persister)
|
||||||
@@ -66,7 +73,7 @@ func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error)
|
|||||||
if perr != nil {
|
if perr != nil {
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
|
logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
|
||||||
}
|
}
|
||||||
return rqs, ErrEngineType
|
return rqs, errors.ErrEngineType
|
||||||
}
|
}
|
||||||
en = en.WithFirst(f.hn.Init)
|
en = en.WithFirst(f.hn.Init)
|
||||||
if rqs.Config.EngineDebug {
|
if rqs.Config.EngineDebug {
|
||||||
@@ -88,13 +95,13 @@ func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error)
|
|||||||
return rqs, nil
|
return rqs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) Output(rqs RequestSession) (RequestSession, error) {
|
func(f *BaseSessionHandler) Output(rqs request.RequestSession) (request.RequestSession, error) {
|
||||||
var err error
|
var err error
|
||||||
_, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer)
|
_, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer)
|
||||||
return rqs, err
|
return rqs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) Reset(rqs RequestSession) (RequestSession, error) {
|
func(f *BaseSessionHandler) Reset(rqs request.RequestSession) (request.RequestSession, error) {
|
||||||
defer f.provider.Put(rqs.Config.SessionId, rqs.Storage)
|
defer f.provider.Put(rqs.Config.SessionId, rqs.Storage)
|
||||||
return rqs, rqs.Engine.Finish()
|
return rqs, rqs.Engine.Finish()
|
||||||
}
|
}
|
||||||
@@ -103,6 +110,6 @@ func(f *BaseSessionHandler) GetConfig() engine.Config {
|
|||||||
return f.cfgTemplate
|
return f.cfgTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) GetRequestParser() RequestParser {
|
func(f *BaseSessionHandler) GetRequestParser() request.RequestParser {
|
||||||
return f.rp
|
return f.rp
|
||||||
}
|
}
|
||||||
34
internal/args/lang.go
Normal file
34
internal/args/lang.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package args
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/lang"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LangVar struct {
|
||||||
|
v []lang.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
func(lv *LangVar) Set(s string) error {
|
||||||
|
v, err := lang.LanguageFromCode(s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lv.v = append(lv.v, v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func(lv *LangVar) String() string {
|
||||||
|
var s []string
|
||||||
|
for _, v := range(lv.v) {
|
||||||
|
s = append(s, v.Code)
|
||||||
|
}
|
||||||
|
return strings.Join(s, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func(lv *LangVar) Langs() []lang.Language {
|
||||||
|
return lv.v
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package at
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/common"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ATRequestParser struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (arp *ATRequestParser) GetSessionId(ctx context.Context, rq any) (string, error) {
|
|
||||||
rqv, ok := rq.(*http.Request)
|
|
||||||
if !ok {
|
|
||||||
logg.Warnf("got an invalid request", "req", rq)
|
|
||||||
return "", handlers.ErrInvalidRequest
|
|
||||||
}
|
|
||||||
// Capture body (if any) for logging
|
|
||||||
body, err := io.ReadAll(rqv.Body)
|
|
||||||
if err != nil {
|
|
||||||
logg.Warnf("failed to read request body", "err", err)
|
|
||||||
return "", fmt.Errorf("failed to read request body: %v", err)
|
|
||||||
}
|
|
||||||
// Reset the body for further reading
|
|
||||||
rqv.Body = io.NopCloser(bytes.NewReader(body))
|
|
||||||
|
|
||||||
// Log the body as JSON
|
|
||||||
bodyLog := map[string]string{"body": string(body)}
|
|
||||||
logBytes, err := json.Marshal(bodyLog)
|
|
||||||
if err != nil {
|
|
||||||
logg.Warnf("failed to marshal request body", "err", err)
|
|
||||||
} else {
|
|
||||||
decodedStr := string(logBytes)
|
|
||||||
sessionId, err := extractATSessionId(decodedStr)
|
|
||||||
if err != nil {
|
|
||||||
ctx = context.WithValue(ctx, "AT-SessionId", sessionId)
|
|
||||||
}
|
|
||||||
logg.DebugCtxf(ctx, "Received request:", decodedStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := rqv.ParseForm(); err != nil {
|
|
||||||
logg.Warnf("failed to parse form data", "err", err)
|
|
||||||
return "", fmt.Errorf("failed to parse form data: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
phoneNumber := rqv.FormValue("phoneNumber")
|
|
||||||
if phoneNumber == "" {
|
|
||||||
return "", fmt.Errorf("no phone number found")
|
|
||||||
}
|
|
||||||
|
|
||||||
formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
|
|
||||||
if err != nil {
|
|
||||||
logg.Warnf("failed to format phone number", "err", err)
|
|
||||||
return "", fmt.Errorf("failed to format number")
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedNumber, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (arp *ATRequestParser) GetInput(rq any) ([]byte, error) {
|
|
||||||
rqv, ok := rq.(*http.Request)
|
|
||||||
if !ok {
|
|
||||||
return nil, handlers.ErrInvalidRequest
|
|
||||||
}
|
|
||||||
if err := rqv.ParseForm(); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse form data: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
text := rqv.FormValue("text")
|
|
||||||
|
|
||||||
parts := strings.Split(text, "*")
|
|
||||||
if len(parts) == 0 {
|
|
||||||
return nil, fmt.Errorf("no input found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(parts[len(parts)-1]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseQueryParams(query string) map[string]string {
|
|
||||||
params := make(map[string]string)
|
|
||||||
|
|
||||||
queryParams := strings.Split(query, "&")
|
|
||||||
for _, param := range queryParams {
|
|
||||||
// Split each key-value pair by '='
|
|
||||||
parts := strings.SplitN(param, "=", 2)
|
|
||||||
if len(parts) == 2 {
|
|
||||||
params[parts[0]] = parts[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractATSessionId(decodedStr string) (string, error) {
|
|
||||||
var data map[string]string
|
|
||||||
err := json.Unmarshal([]byte(decodedStr), &data)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logg.Errorf("Error unmarshalling JSON: %v", err)
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
decodedBody, err := url.QueryUnescape(data["body"])
|
|
||||||
if err != nil {
|
|
||||||
logg.Errorf("Error URL-decoding body: %v", err)
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
params := parseQueryParams(decodedBody)
|
|
||||||
|
|
||||||
sessionId := params["sessionId"]
|
|
||||||
return sessionId, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package at
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
logg = logging.NewVanilla().WithDomain("atserver").WithContextKey("SessionId").WithContextKey("AT-SessionId")
|
|
||||||
)
|
|
||||||
|
|
||||||
type ATSessionHandler struct {
|
|
||||||
*httpserver.SessionHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewATSessionHandler(h handlers.RequestHandler) *ATSessionHandler {
|
|
||||||
return &ATSessionHandler{
|
|
||||||
SessionHandler: httpserver.ToSessionHandler(h),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ash *ATSessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
||||||
var code int
|
|
||||||
var err error
|
|
||||||
|
|
||||||
rqs := handlers.RequestSession{
|
|
||||||
Ctx: req.Context(),
|
|
||||||
Writer: w,
|
|
||||||
}
|
|
||||||
|
|
||||||
rp := ash.GetRequestParser()
|
|
||||||
cfg := ash.GetConfig()
|
|
||||||
cfg.SessionId, err = rp.GetSessionId(req.Context(), req)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
|
|
||||||
ash.WriteError(w, 400, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rqs.Config = cfg
|
|
||||||
rqs.Input, err = rp.GetInput(req)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "header processing error", err)
|
|
||||||
ash.WriteError(w, 400, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rqs, err = ash.Process(rqs)
|
|
||||||
switch err {
|
|
||||||
case nil: // set code to 200 if no err
|
|
||||||
code = 200
|
|
||||||
case handlers.ErrStorage, handlers.ErrEngineInit, handlers.ErrEngineExec, handlers.ErrEngineType:
|
|
||||||
code = 500
|
|
||||||
default:
|
|
||||||
code = 500
|
|
||||||
}
|
|
||||||
|
|
||||||
if code != 200 {
|
|
||||||
ash.WriteError(w, 500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.WriteHeader(200)
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
rqs, err = ash.Output(rqs)
|
|
||||||
if err != nil {
|
|
||||||
ash.WriteError(w, 500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rqs, err = ash.Reset(rqs)
|
|
||||||
if err != nil {
|
|
||||||
ash.WriteError(w, 500, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ash *ATSessionHandler) Output(rqs handlers.RequestSession) (handlers.RequestSession, error) {
|
|
||||||
var err error
|
|
||||||
var prefix string
|
|
||||||
|
|
||||||
if rqs.Continue {
|
|
||||||
prefix = "CON "
|
|
||||||
} else {
|
|
||||||
prefix = "END "
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = io.WriteString(rqs.Writer, prefix)
|
|
||||||
if err != nil {
|
|
||||||
return rqs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = rqs.Engine.Flush(rqs.Ctx, rqs.Writer)
|
|
||||||
return rqs, err
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
package at
|
|
||||||
|
|
||||||
import (
|
|
||||||
"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/testutil/mocks/httpmocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
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.FlushFunc = 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{
|
|
||||||
FlushFunc: 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{
|
|
||||||
FlushFunc: func(context.Context, io.Writer) (int, error) {
|
|
||||||
return 0, nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Writer: &httpmocks.MockWriter{},
|
|
||||||
},
|
|
||||||
expectedPrefix: "END ",
|
|
||||||
expectedError: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Flush error",
|
|
||||||
input: handlers.RequestSession{
|
|
||||||
Continue: true,
|
|
||||||
Engine: &httpmocks.MockEngine{
|
|
||||||
FlushFunc: 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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -7,22 +7,16 @@ import (
|
|||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
"git.grassecon.net/urdt/ussd/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logg = logging.NewVanilla().WithDomain("httpserver")
|
logg = logging.NewVanilla().WithDomain("httpserver")
|
||||||
)
|
)
|
||||||
|
|
||||||
type SessionHandler struct {
|
type SessionHandler request.SessionHandler
|
||||||
handlers.RequestHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToSessionHandler(h handlers.RequestHandler) *SessionHandler {
|
|
||||||
return &SessionHandler{
|
|
||||||
RequestHandler: h,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: duplicated
|
||||||
func (f *SessionHandler) WriteError(w http.ResponseWriter, code int, err error) {
|
func (f *SessionHandler) WriteError(w http.ResponseWriter, code int, err error) {
|
||||||
s := err.Error()
|
s := err.Error()
|
||||||
w.Header().Set("Content-Length", strconv.Itoa(len(s)))
|
w.Header().Set("Content-Length", strconv.Itoa(len(s)))
|
||||||
@@ -39,7 +33,7 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
var err error
|
var err error
|
||||||
var perr error
|
var perr error
|
||||||
|
|
||||||
rqs := handlers.RequestSession{
|
rqs := request.RequestSession{
|
||||||
Ctx: req.Context(),
|
Ctx: req.Context(),
|
||||||
Writer: w,
|
Writer: w,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import (
|
|||||||
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
"git.defalsify.org/vise.git/engine"
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/testutil/mocks/httpmocks"
|
"git.grassecon.net/urdt/ussd/testutil/mocks/httpmocks"
|
||||||
|
"git.grassecon.net/urdt/ussd/request"
|
||||||
)
|
)
|
||||||
|
|
||||||
// invalidRequestType is a custom type to test invalid request scenarios
|
// invalidRequestType is a custom type to test invalid request scenarios
|
||||||
@@ -81,16 +82,16 @@ func TestSessionHandler_ServeHTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockRequestHandler := &httpmocks.MockRequestHandler{
|
mockRequestHandler := &httpmocks.MockRequestHandler{
|
||||||
ProcessFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
ProcessFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
||||||
return rs, tt.processErr
|
return rs, tt.processErr
|
||||||
},
|
},
|
||||||
OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
OutputFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
||||||
return rs, tt.outputErr
|
return rs, tt.outputErr
|
||||||
},
|
},
|
||||||
ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
ResetFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
||||||
return rs, tt.resetErr
|
return rs, tt.resetErr
|
||||||
},
|
},
|
||||||
GetRequestParserFunc: func() handlers.RequestParser {
|
GetRequestParserFunc: func() request.RequestParser {
|
||||||
return mockRequestParser
|
return mockRequestParser
|
||||||
},
|
},
|
||||||
GetConfigFunc: func() engine.Config {
|
GetConfigFunc: func() engine.Config {
|
||||||
@@ -98,7 +99,10 @@ func TestSessionHandler_ServeHTTP(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionHandler := ToSessionHandler(mockRequestHandler)
|
sessionHandler := &SessionHandler{
|
||||||
|
RequestHandler: mockRequestHandler,
|
||||||
|
}
|
||||||
|
//sessionHandler := request.ToSessionHandler(mockRequestHandler)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(tt.input))
|
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewBuffer(tt.input))
|
||||||
req.Header.Set("X-Vise-Session", tt.sessionID)
|
req.Header.Set("X-Vise-Session", tt.sessionID)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"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"
|
"git.grassecon.net/urdt/ussd/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
)
|
)
|
||||||
@@ -71,6 +71,20 @@ func(a *auther) Get(k []byte) (string, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SshRunner struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Cfg engine.Config
|
||||||
|
FlagFile string
|
||||||
|
Conn storage.ConnData
|
||||||
|
ResourceDir string
|
||||||
|
Debug bool
|
||||||
|
SrvKeyFile string
|
||||||
|
Host string
|
||||||
|
Port uint
|
||||||
|
wg sync.WaitGroup
|
||||||
|
lst net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error {
|
func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error {
|
||||||
if ch == nil {
|
if ch == nil {
|
||||||
return errors.New("nil channel")
|
return errors.New("nil channel")
|
||||||
@@ -128,32 +142,13 @@ func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChanne
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type SshRunner struct {
|
|
||||||
Ctx context.Context
|
|
||||||
Cfg engine.Config
|
|
||||||
FlagFile string
|
|
||||||
DbDir string
|
|
||||||
ResourceDir string
|
|
||||||
Debug bool
|
|
||||||
SrvKeyFile string
|
|
||||||
Host string
|
|
||||||
Port uint
|
|
||||||
wg sync.WaitGroup
|
|
||||||
lst net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshRunner) Stop() error {
|
func(s *SshRunner) Stop() error {
|
||||||
return s.lst.Close()
|
return s.lst.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
||||||
ctx := s.Ctx
|
ctx := s.Ctx
|
||||||
menuStorageService := storage.NewMenuStorageService(s.DbDir, s.ResourceDir)
|
menuStorageService := storage.NewMenuStorageService(s.Conn, s.ResourceDir)
|
||||||
|
|
||||||
err := menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
rs, err := menuStorageService.GetResource(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
69
internal/storage/parse.go
Normal file
69
internal/storage/parse.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DBTYPE_MEM = iota
|
||||||
|
DBTYPE_GDBM
|
||||||
|
DBTYPE_POSTGRES
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnData struct {
|
||||||
|
typ int
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnData) DbType() int {
|
||||||
|
return cd.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnData) String() string {
|
||||||
|
return cd.str
|
||||||
|
}
|
||||||
|
|
||||||
|
func probePostgres(s string) (string, bool) {
|
||||||
|
v, err := url.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
if v.Scheme != "postgres" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func probeGdbm(s string) (string, bool) {
|
||||||
|
if !path.IsAbs(s) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
s = path.Clean(s)
|
||||||
|
return s, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToConnData(connStr string) (ConnData, error) {
|
||||||
|
var o ConnData
|
||||||
|
|
||||||
|
if connStr == "" {
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := probePostgres(connStr)
|
||||||
|
if ok {
|
||||||
|
o.typ = DBTYPE_POSTGRES
|
||||||
|
o.str = v
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok = probeGdbm(connStr)
|
||||||
|
if ok {
|
||||||
|
o.typ = DBTYPE_GDBM
|
||||||
|
o.str = v
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return o, fmt.Errorf("invalid connection string: %s", connStr)
|
||||||
|
}
|
||||||
28
internal/storage/parse_test.go
Normal file
28
internal/storage/parse_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseConnStr(t *testing.T) {
|
||||||
|
_, err := ToConnData("postgres://foo:bar@localhost:5432/baz")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = ToConnData("/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = ToConnData("/foo/bar/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = ToConnData("foo/bar")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
_, err = ToConnData("http://foo/bar")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,10 @@ import (
|
|||||||
"git.defalsify.org/vise.git/db"
|
"git.defalsify.org/vise.git/db"
|
||||||
fsdb "git.defalsify.org/vise.git/db/fs"
|
fsdb "git.defalsify.org/vise.git/db/fs"
|
||||||
"git.defalsify.org/vise.git/db/postgres"
|
"git.defalsify.org/vise.git/db/postgres"
|
||||||
|
"git.defalsify.org/vise.git/lang"
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
"git.defalsify.org/vise.git/persist"
|
"git.defalsify.org/vise.git/persist"
|
||||||
"git.defalsify.org/vise.git/resource"
|
"git.defalsify.org/vise.git/resource"
|
||||||
"git.grassecon.net/urdt/ussd/initializers"
|
|
||||||
gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/db/gdbm"
|
gdbmstorage "git.grassecon.net/urdt/ussd/internal/storage/db/gdbm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,63 +24,54 @@ type StorageService interface {
|
|||||||
GetPersister(ctx context.Context) (*persist.Persister, error)
|
GetPersister(ctx context.Context) (*persist.Persister, error)
|
||||||
GetUserdataDb(ctx context.Context) db.Db
|
GetUserdataDb(ctx context.Context) db.Db
|
||||||
GetResource(ctx context.Context) (resource.Resource, error)
|
GetResource(ctx context.Context) (resource.Resource, error)
|
||||||
EnsureDbDir() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MenuStorageService struct {
|
type MenuStorageService struct {
|
||||||
dbDir string
|
conn ConnData
|
||||||
resourceDir string
|
resourceDir string
|
||||||
|
poResource resource.Resource
|
||||||
resourceStore db.Db
|
resourceStore db.Db
|
||||||
stateStore db.Db
|
stateStore db.Db
|
||||||
userDataStore db.Db
|
userDataStore db.Db
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildConnStr() string {
|
func NewMenuStorageService(conn ConnData, resourceDir string) *MenuStorageService {
|
||||||
host := initializers.GetEnv("DB_HOST", "localhost")
|
|
||||||
user := initializers.GetEnv("DB_USER", "postgres")
|
|
||||||
password := initializers.GetEnv("DB_PASSWORD", "")
|
|
||||||
dbName := initializers.GetEnv("DB_NAME", "")
|
|
||||||
port := initializers.GetEnv("DB_PORT", "5432")
|
|
||||||
|
|
||||||
connString := fmt.Sprintf(
|
|
||||||
"postgres://%s:%s@%s:%s/%s",
|
|
||||||
user, password, host, port, dbName,
|
|
||||||
)
|
|
||||||
logg.Debugf("pg conn string", "conn", connString)
|
|
||||||
|
|
||||||
return connString
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMenuStorageService(dbDir string, resourceDir string) *MenuStorageService {
|
|
||||||
return &MenuStorageService{
|
return &MenuStorageService{
|
||||||
dbDir: dbDir,
|
conn: conn,
|
||||||
resourceDir: resourceDir,
|
resourceDir: resourceDir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, fileName string) (db.Db, error) {
|
func (ms *MenuStorageService) WithResourceDir(resourceDir string) *MenuStorageService {
|
||||||
database, ok := ctx.Value("Database").(string)
|
ms.resourceDir = resourceDir
|
||||||
if !ok {
|
return ms
|
||||||
return nil, fmt.Errorf("failed to select the database")
|
}
|
||||||
}
|
|
||||||
|
func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, section string) (db.Db, error) {
|
||||||
|
var newDb db.Db
|
||||||
|
var err error
|
||||||
|
|
||||||
if existingDb != nil {
|
if existingDb != nil {
|
||||||
return existingDb, nil
|
return existingDb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var newDb db.Db
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if database == "postgres" {
|
connStr := ms.conn.String()
|
||||||
|
dbTyp := ms.conn.DbType()
|
||||||
|
if dbTyp == DBTYPE_POSTGRES {
|
||||||
newDb = postgres.NewPgDb()
|
newDb = postgres.NewPgDb()
|
||||||
connStr := buildConnStr()
|
} else if dbTyp == DBTYPE_GDBM {
|
||||||
err = newDb.Connect(ctx, connStr)
|
err = ms.ensureDbDir()
|
||||||
} else {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
connStr = path.Join(connStr, section)
|
||||||
newDb = gdbmstorage.NewThreadGdbmDb()
|
newDb = gdbmstorage.NewThreadGdbmDb()
|
||||||
storeFile := path.Join(ms.dbDir, fileName)
|
} else {
|
||||||
err = newDb.Connect(ctx, storeFile)
|
return nil, fmt.Errorf("unsupported connection string: '%s'\n", ms.conn.String())
|
||||||
}
|
}
|
||||||
|
logg.DebugCtxf(ctx, "connecting to db", "conn", connStr)
|
||||||
|
err = newDb.Connect(ctx, connStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -88,6 +79,28 @@ func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.D
|
|||||||
return newDb, nil
|
return newDb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithGettext triggers use of gettext for translation of templates and menus.
|
||||||
|
//
|
||||||
|
// The first language in `lns` will be used as default language, to resolve node keys to
|
||||||
|
// language strings.
|
||||||
|
//
|
||||||
|
// If `lns` is an empty array, gettext will not be used.
|
||||||
|
func (ms *MenuStorageService) WithGettext(path string, lns []lang.Language) *MenuStorageService {
|
||||||
|
if len(lns) == 0 {
|
||||||
|
logg.Warnf("Gettext requested but no languages supplied")
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
rs := resource.NewPoResource(lns[0], path)
|
||||||
|
|
||||||
|
for _, ln := range(lns) {
|
||||||
|
rs = rs.WithLanguage(ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
ms.poResource = rs
|
||||||
|
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
|
||||||
func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
|
func (ms *MenuStorageService) GetPersister(ctx context.Context) (*persist.Persister, error) {
|
||||||
stateStore, err := ms.GetStateStore(ctx)
|
stateStore, err := ms.GetStateStore(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -120,6 +133,11 @@ func (ms *MenuStorageService) GetResource(ctx context.Context) (resource.Resourc
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rfs := resource.NewDbResource(ms.resourceStore)
|
rfs := resource.NewDbResource(ms.resourceStore)
|
||||||
|
if ms.poResource != nil {
|
||||||
|
logg.InfoCtxf(ctx, "using poresource for menu and template")
|
||||||
|
rfs.WithMenuGetter(ms.poResource.GetMenu)
|
||||||
|
rfs.WithTemplateGetter(ms.poResource.GetTemplate)
|
||||||
|
}
|
||||||
return rfs, nil
|
return rfs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,8 +155,8 @@ func (ms *MenuStorageService) GetStateStore(ctx context.Context) (db.Db, error)
|
|||||||
return ms.stateStore, nil
|
return ms.stateStore, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MenuStorageService) EnsureDbDir() error {
|
func (ms *MenuStorageService) ensureDbDir() error {
|
||||||
err := os.MkdirAll(ms.dbDir, 0700)
|
err := os.MkdirAll(ms.conn.String(), 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
return fmt.Errorf("state dir create exited with error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"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/handlers"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
|
"git.grassecon.net/urdt/ussd/internal/testutil/testservice"
|
||||||
"git.grassecon.net/urdt/ussd/internal/testutil/testtag"
|
"git.grassecon.net/urdt/ussd/internal/testutil/testtag"
|
||||||
@@ -27,7 +28,6 @@ var (
|
|||||||
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
|
func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
ctx = context.WithValue(ctx, "SessionId", sessionId)
|
||||||
ctx = context.WithValue(ctx, "Database", "gdbm")
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
pfp := path.Join(scriptDir, "pp.csv")
|
||||||
|
|
||||||
var eventChannel = make(chan bool)
|
var eventChannel = make(chan bool)
|
||||||
@@ -39,37 +39,40 @@ func TestEngine(sessionId string) (engine.Engine, func(), chan bool) {
|
|||||||
FlagCount: uint32(128),
|
FlagCount: uint32(128),
|
||||||
}
|
}
|
||||||
|
|
||||||
dbDir := ".test_state"
|
connStr, err := filepath.Abs(".test_state/state.gdbm")
|
||||||
resourceDir := scriptDir
|
|
||||||
menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
|
|
||||||
|
|
||||||
err := menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "connstr err: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
conn, err := storage.ToConnData(connStr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "connstr parse err: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
resourceDir := scriptDir
|
||||||
|
menuStorageService := storage.NewMenuStorageService(conn, resourceDir)
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
rs, err := menuStorageService.GetResource(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "resource error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pe, err := menuStorageService.GetPersister(ctx)
|
pe, err := menuStorageService.GetPersister(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "persister error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
userDataStore, err := menuStorageService.GetUserdataDb(ctx)
|
userDataStore, err := menuStorageService.GetUserdataDb(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "userdb error: %v", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
dbResource, ok := rs.(*resource.DbResource)
|
dbResource, ok := rs.(*resource.DbResource)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Fprintf(os.Stderr, err.Error())
|
fmt.Fprintf(os.Stderr, "dbresource cast error")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
15
internal/testutil/engine_test.go
Normal file
15
internal/testutil/engine_test.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateEngine(t *testing.T) {
|
||||||
|
o, clean, eventC := TestEngine("foo")
|
||||||
|
defer clean()
|
||||||
|
defer func() {
|
||||||
|
<-eventC
|
||||||
|
close(eventC)
|
||||||
|
}()
|
||||||
|
_ = o
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -17,7 +18,6 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
testData = driver.ReadData()
|
testData = driver.ReadData()
|
||||||
testStore = ".test_state"
|
|
||||||
sessionID string
|
sessionID string
|
||||||
src = rand.NewSource(42)
|
src = rand.NewSource(42)
|
||||||
g = rand.New(src)
|
g = rand.New(src)
|
||||||
@@ -25,6 +25,11 @@ var (
|
|||||||
|
|
||||||
var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests")
|
var groupTestFile = flag.String("test-file", "group_test.json", "The test file to use for running the group tests")
|
||||||
|
|
||||||
|
func testStore() string {
|
||||||
|
v, _ := filepath.Abs(".test_state/state.gdbm")
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateSessionId() string {
|
func GenerateSessionId() string {
|
||||||
uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g))
|
uu := uuid.NewGenWithOptions(uuid.WithRandomReader(g))
|
||||||
v, err := uu.NewV4()
|
v, err := uu.NewV4()
|
||||||
@@ -81,8 +86,8 @@ func extractSendAmount(response []byte) string {
|
|||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
sessionID = GenerateSessionId()
|
sessionID = GenerateSessionId()
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := os.RemoveAll(testStore); err != nil {
|
if err := os.RemoveAll(testStore()); err != nil {
|
||||||
log.Fatalf("Failed to delete state store %s: %v", testStore, err)
|
log.Fatalf("Failed to delete state store %s: %v", testStore(), err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
m.Run()
|
m.Run()
|
||||||
|
|||||||
65
request/request.go
Normal file
65
request/request.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.defalsify.org/vise.git/persist"
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("visedriver.request")
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestSession struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Config engine.Config
|
||||||
|
Engine engine.Engine
|
||||||
|
Input []byte
|
||||||
|
Storage *storage.Storage
|
||||||
|
Writer io.Writer
|
||||||
|
Continue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: seems like can remove this.
|
||||||
|
type RequestParser interface {
|
||||||
|
GetSessionId(ctx context.Context, rq any) (string, error)
|
||||||
|
GetInput(rq any) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RequestHandler interface {
|
||||||
|
GetConfig() engine.Config
|
||||||
|
GetRequestParser() RequestParser
|
||||||
|
GetEngine(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
|
||||||
|
Process(rs RequestSession) (RequestSession, error)
|
||||||
|
Output(rs RequestSession) (RequestSession, error)
|
||||||
|
Reset(rs RequestSession) (RequestSession, error)
|
||||||
|
Shutdown()
|
||||||
|
}
|
||||||
|
type SessionHandler struct {
|
||||||
|
RequestHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *SessionHandler) WriteError(w http.ResponseWriter, code int, err error) {
|
||||||
|
s := err.Error()
|
||||||
|
w.Header().Set("Content-Length", strconv.Itoa(len(s)))
|
||||||
|
w.WriteHeader(code)
|
||||||
|
_, err = w.Write([]byte(s))
|
||||||
|
if err != nil {
|
||||||
|
logg.Errorf("error writing error!!", "err", err, "olderr", s)
|
||||||
|
w.WriteHeader(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToSessionHandler(h RequestHandler) *SessionHandler {
|
||||||
|
return &SessionHandler{
|
||||||
|
RequestHandler: h,
|
||||||
|
}
|
||||||
|
}
|
||||||
47
testutil/mocks/httpmocks/requesthandlermock.go
Normal file
47
testutil/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/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockRequestHandler implements request.RequestHandler interface for testing
|
||||||
|
type MockRequestHandler struct {
|
||||||
|
ProcessFunc func(request.RequestSession) (request.RequestSession, error)
|
||||||
|
GetConfigFunc func() engine.Config
|
||||||
|
GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
|
||||||
|
OutputFunc func(rs request.RequestSession) (request.RequestSession, error)
|
||||||
|
ResetFunc func(rs request.RequestSession) (request.RequestSession, error)
|
||||||
|
ShutdownFunc func()
|
||||||
|
GetRequestParserFunc func() request.RequestParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Process(rqs request.RequestSession) (request.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 request.RequestSession) (request.RequestSession, error) {
|
||||||
|
return m.OutputFunc(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Reset(rs request.RequestSession) (request.RequestSession, error) {
|
||||||
|
return m.ResetFunc(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) Shutdown() {
|
||||||
|
m.ShutdownFunc()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockRequestHandler) GetRequestParser() request.RequestParser {
|
||||||
|
return m.GetRequestParserFunc()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user