Compare commits
No commits in common. "6789c4f550fa98468bdc21b007d3866c661023b4" and "51b6fc0dde431b6489e5ceb26a6e0cc2c66c6b51" have entirely different histories.
6789c4f550
...
51b6fc0dde
163
cmd/africastalking/main.go
Normal file
163
cmd/africastalking/main.go
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
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"
|
||||||
|
"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 connStr string
|
||||||
|
var resourceDir string
|
||||||
|
var size uint
|
||||||
|
var database string
|
||||||
|
var engineDebug bool
|
||||||
|
var host string
|
||||||
|
var port uint
|
||||||
|
var err error
|
||||||
|
|
||||||
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
|
flag.StringVar(&connStr, "c", "", "connection string")
|
||||||
|
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()
|
||||||
|
|
||||||
|
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", "build", build, "conn", connData, "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(connData, resourceDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := menuStorageService.GetResource(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
|
Context: ctx,
|
||||||
|
}
|
||||||
|
bsh := handlers.NewBaseSessionHandler(cfg, rs, stateStore, userdataStore, rp, hl)
|
||||||
|
sh := at.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)
|
||||||
|
}
|
||||||
|
}
|
@ -15,10 +15,9 @@ import (
|
|||||||
|
|
||||||
"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/handlers"
|
"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/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
"git.grassecon.net/urdt/ussd/request"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -139,7 +138,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 := request.RequestSession{
|
rqs := handlers.RequestSession{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
Writer: os.Stdout,
|
Writer: os.Stdout,
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
"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/handlers"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
httpserver "git.grassecon.net/urdt/ussd/internal/http"
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
"git.grassecon.net/urdt/ussd/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
@ -123,11 +123,7 @@ 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)
|
||||||
// TODO: less hacky way of making session handler
|
sh := httpserver.ToSessionHandler(bsh)
|
||||||
//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,
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"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/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/remote"
|
"git.grassecon.net/urdt/ussd/remote"
|
||||||
)
|
)
|
||||||
|
@ -11,10 +11,6 @@ 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
|
||||||
}
|
}
|
||||||
@ -40,12 +36,6 @@ func NewStorageService(conn storage.ConnData) (*StorageService, error) {
|
|||||||
return svc, nil
|
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) {
|
||||||
return ss.svc.GetPersister(ctx)
|
return ss.svc.GetPersister(ctx)
|
||||||
}
|
}
|
||||||
@ -57,7 +47,3 @@ func(ss *StorageService) GetUserdataDb(ctx context.Context) (db.Db, error) {
|
|||||||
func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) {
|
func(ss *StorageService) GetResource(ctx context.Context) (resource.Resource, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func(ss *StorageService) GetStateStore(ctx context.Context) (db.Db, error) {
|
|
||||||
return ss.svc.GetStateStore(ctx)
|
|
||||||
}
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
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,27 +5,20 @@ 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 request.RequestParser
|
rp 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 request.RequestParser, hn *ussd.Handlers) *BaseSessionHandler {
|
func NewBaseSessionHandler(cfg engine.Config, rs resource.Resource, stateDb db.Db, userdataDb db.Db, rp RequestParser, hn *ussd.Handlers) *BaseSessionHandler {
|
||||||
return &BaseSessionHandler{
|
return &BaseSessionHandler{
|
||||||
cfgTemplate: cfg,
|
cfgTemplate: cfg,
|
||||||
rs: rs,
|
rs: rs,
|
||||||
@ -48,7 +41,7 @@ func(f *BaseSessionHandler) GetEngine(cfg engine.Config, rs resource.Resource, p
|
|||||||
return en
|
return en
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.RequestSession, error) {
|
func(f *BaseSessionHandler) Process(rqs RequestSession) (RequestSession, error) {
|
||||||
var r bool
|
var r bool
|
||||||
var err error
|
var err error
|
||||||
var ok bool
|
var ok bool
|
||||||
@ -58,7 +51,7 @@ func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.Request
|
|||||||
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, errors.ErrStorage
|
return rqs, ErrStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
f.hn = f.hn.WithPersister(rqs.Storage.Persister)
|
f.hn = f.hn.WithPersister(rqs.Storage.Persister)
|
||||||
@ -73,7 +66,7 @@ func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.Request
|
|||||||
if perr != nil {
|
if perr != nil {
|
||||||
logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
|
logg.ErrorCtxf(rqs.Ctx, "", "storage put error", perr)
|
||||||
}
|
}
|
||||||
return rqs, errors.ErrEngineType
|
return rqs, ErrEngineType
|
||||||
}
|
}
|
||||||
en = en.WithFirst(f.hn.Init)
|
en = en.WithFirst(f.hn.Init)
|
||||||
if rqs.Config.EngineDebug {
|
if rqs.Config.EngineDebug {
|
||||||
@ -95,13 +88,13 @@ func(f *BaseSessionHandler) Process(rqs request.RequestSession) (request.Request
|
|||||||
return rqs, nil
|
return rqs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) Output(rqs request.RequestSession) (request.RequestSession, error) {
|
func(f *BaseSessionHandler) Output(rqs RequestSession) (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 request.RequestSession) (request.RequestSession, error) {
|
func(f *BaseSessionHandler) Reset(rqs RequestSession) (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()
|
||||||
}
|
}
|
||||||
@ -110,6 +103,6 @@ func(f *BaseSessionHandler) GetConfig() engine.Config {
|
|||||||
return f.cfgTemplate
|
return f.cfgTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
func(f *BaseSessionHandler) GetRequestParser() request.RequestParser {
|
func(f *BaseSessionHandler) GetRequestParser() RequestParser {
|
||||||
return f.rp
|
return f.rp
|
||||||
}
|
}
|
@ -1,9 +1,20 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.defalsify.org/vise.git/engine"
|
||||||
|
"git.defalsify.org/vise.git/resource"
|
||||||
|
"git.defalsify.org/vise.git/persist"
|
||||||
"git.defalsify.org/vise.git/logging"
|
"git.defalsify.org/vise.git/logging"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logg = logging.NewVanilla().WithDomain("handlers")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -16,6 +27,28 @@ var (
|
|||||||
ErrEngineExec = errors.New("engine exec fail")
|
ErrEngineExec = errors.New("engine exec fail")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type RequestSession struct {
|
||||||
logg = logging.NewVanilla().WithDomain("handlers")
|
Ctx context.Context
|
||||||
)
|
Config engine.Config
|
||||||
|
Engine engine.Engine
|
||||||
|
Input []byte
|
||||||
|
Storage *storage.Storage
|
||||||
|
Writer io.Writer
|
||||||
|
Continue bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: seems like can remove this.
|
||||||
|
type RequestParser interface {
|
||||||
|
GetSessionId(rq any) (string, error)
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
121
internal/http/at/parse.go
Normal file
121
internal/http/at/parse.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package at
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.grassecon.net/urdt/ussd/common"
|
||||||
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ATRequestParser struct {
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (arp *ATRequestParser) GetSessionId(rq any) (string, error) {
|
||||||
|
rqv, ok := rq.(*http.Request)
|
||||||
|
if !ok {
|
||||||
|
logg.Warnf("got an invalid request", "req", rq)
|
||||||
|
return "", handlers.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture body (if any) for logging
|
||||||
|
body, err := io.ReadAll(rqv.Body)
|
||||||
|
if err != nil {
|
||||||
|
logg.Warnf("failed to read request body", "err", err)
|
||||||
|
return "", fmt.Errorf("failed to read request body: %v", err)
|
||||||
|
}
|
||||||
|
// Reset the body for further reading
|
||||||
|
rqv.Body = io.NopCloser(bytes.NewReader(body))
|
||||||
|
|
||||||
|
// Log the body as JSON
|
||||||
|
bodyLog := map[string]string{"body": string(body)}
|
||||||
|
logBytes, err := json.Marshal(bodyLog)
|
||||||
|
if err != nil {
|
||||||
|
logg.Warnf("failed to marshal request body", "err", err)
|
||||||
|
} else {
|
||||||
|
decodedStr := string(logBytes)
|
||||||
|
sessionId, err := extractATSessionId(decodedStr)
|
||||||
|
if err != nil {
|
||||||
|
context.WithValue(arp.Context, "at-session-id", sessionId)
|
||||||
|
}
|
||||||
|
logg.Debugf("Received request:", decodedStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rqv.ParseForm(); err != nil {
|
||||||
|
logg.Warnf("failed to parse form data", "err", err)
|
||||||
|
return "", fmt.Errorf("failed to parse form data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
phoneNumber := rqv.FormValue("phoneNumber")
|
||||||
|
if phoneNumber == "" {
|
||||||
|
return "", fmt.Errorf("no phone number found")
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
|
||||||
|
if err != nil {
|
||||||
|
logg.Warnf("failed to format phone number", "err", err)
|
||||||
|
return "", fmt.Errorf("failed to format number")
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (arp *ATRequestParser) GetInput(rq any) ([]byte, error) {
|
||||||
|
rqv, ok := rq.(*http.Request)
|
||||||
|
if !ok {
|
||||||
|
return nil, handlers.ErrInvalidRequest
|
||||||
|
}
|
||||||
|
if err := rqv.ParseForm(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse form data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
text := rqv.FormValue("text")
|
||||||
|
|
||||||
|
parts := strings.Split(text, "*")
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return nil, fmt.Errorf("no input found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(parts[len(parts)-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseQueryParams(query string) map[string]string {
|
||||||
|
params := make(map[string]string)
|
||||||
|
|
||||||
|
queryParams := strings.Split(query, "&")
|
||||||
|
for _, param := range queryParams {
|
||||||
|
// Split each key-value pair by '='
|
||||||
|
parts := strings.SplitN(param, "=", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
|
params[parts[0]] = parts[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractATSessionId(decodedStr string) (string, error) {
|
||||||
|
var data map[string]string
|
||||||
|
err := json.Unmarshal([]byte(decodedStr), &data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logg.Errorf("Error unmarshalling JSON: %v", err)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
decodedBody, err := url.QueryUnescape(data["body"])
|
||||||
|
if err != nil {
|
||||||
|
logg.Errorf("Error URL-decoding body: %v", err)
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
params := parseQueryParams(decodedBody)
|
||||||
|
|
||||||
|
sessionId := params["sessionId"]
|
||||||
|
return sessionId, nil
|
||||||
|
|
||||||
|
}
|
98
internal/http/at/server.go
Normal file
98
internal/http/at/server.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
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")
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
}
|
234
internal/http/at/server_test.go
Normal file
234
internal/http/at/server_test.go
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
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,16 +7,22 @@ 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 request.SessionHandler
|
type SessionHandler struct {
|
||||||
|
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)))
|
||||||
@ -33,7 +39,7 @@ func (f *SessionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
var err error
|
var err error
|
||||||
var perr error
|
var perr error
|
||||||
|
|
||||||
rqs := request.RequestSession{
|
rqs := handlers.RequestSession{
|
||||||
Ctx: req.Context(),
|
Ctx: req.Context(),
|
||||||
Writer: w,
|
Writer: w,
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ 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/internal/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 +80,16 @@ func TestSessionHandler_ServeHTTP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mockRequestHandler := &httpmocks.MockRequestHandler{
|
mockRequestHandler := &httpmocks.MockRequestHandler{
|
||||||
ProcessFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
ProcessFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return rs, tt.processErr
|
return rs, tt.processErr
|
||||||
},
|
},
|
||||||
OutputFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
OutputFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return rs, tt.outputErr
|
return rs, tt.outputErr
|
||||||
},
|
},
|
||||||
ResetFunc: func(rs request.RequestSession) (request.RequestSession, error) {
|
ResetFunc: func(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return rs, tt.resetErr
|
return rs, tt.resetErr
|
||||||
},
|
},
|
||||||
GetRequestParserFunc: func() request.RequestParser {
|
GetRequestParserFunc: func() handlers.RequestParser {
|
||||||
return mockRequestParser
|
return mockRequestParser
|
||||||
},
|
},
|
||||||
GetConfigFunc: func() engine.Config {
|
GetConfigFunc: func() engine.Config {
|
||||||
@ -98,10 +97,7 @@ func TestSessionHandler_ServeHTTP(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionHandler := &SessionHandler{
|
sessionHandler := ToSessionHandler(mockRequestHandler)
|
||||||
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)
|
||||||
|
@ -40,11 +40,6 @@ func NewMenuStorageService(conn ConnData, resourceDir string) *MenuStorageServic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MenuStorageService) WithResourceDir(resourceDir string) *MenuStorageService {
|
|
||||||
ms.resourceDir = resourceDir
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, section string) (db.Db, error) {
|
func (ms *MenuStorageService) getOrCreateDb(ctx context.Context, existingDb db.Db, section string) (db.Db, error) {
|
||||||
var newDb db.Db
|
var newDb db.Db
|
||||||
var err error
|
var err error
|
||||||
|
@ -11,7 +11,7 @@ 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.grassecon.net/urdt/ussd/handlers"
|
"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/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"
|
||||||
|
@ -4,21 +4,21 @@ 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.grassecon.net/urdt/ussd/request"
|
"git.grassecon.net/urdt/ussd/internal/handlers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockRequestHandler implements request.RequestHandler interface for testing
|
// MockRequestHandler implements handlers.RequestHandler interface for testing
|
||||||
type MockRequestHandler struct {
|
type MockRequestHandler struct {
|
||||||
ProcessFunc func(request.RequestSession) (request.RequestSession, error)
|
ProcessFunc func(handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
GetConfigFunc func() engine.Config
|
GetConfigFunc func() engine.Config
|
||||||
GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
|
GetEngineFunc func(cfg engine.Config, rs resource.Resource, pe *persist.Persister) engine.Engine
|
||||||
OutputFunc func(rs request.RequestSession) (request.RequestSession, error)
|
OutputFunc func(rs handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
ResetFunc func(rs request.RequestSession) (request.RequestSession, error)
|
ResetFunc func(rs handlers.RequestSession) (handlers.RequestSession, error)
|
||||||
ShutdownFunc func()
|
ShutdownFunc func()
|
||||||
GetRequestParserFunc func() request.RequestParser
|
GetRequestParserFunc func() handlers.RequestParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRequestHandler) Process(rqs request.RequestSession) (request.RequestSession, error) {
|
func (m *MockRequestHandler) Process(rqs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return m.ProcessFunc(rqs)
|
return m.ProcessFunc(rqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,11 +30,11 @@ func (m *MockRequestHandler) GetEngine(cfg engine.Config, rs resource.Resource,
|
|||||||
return m.GetEngineFunc(cfg, rs, pe)
|
return m.GetEngineFunc(cfg, rs, pe)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRequestHandler) Output(rs request.RequestSession) (request.RequestSession, error) {
|
func (m *MockRequestHandler) Output(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return m.OutputFunc(rs)
|
return m.OutputFunc(rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRequestHandler) Reset(rs request.RequestSession) (request.RequestSession, error) {
|
func (m *MockRequestHandler) Reset(rs handlers.RequestSession) (handlers.RequestSession, error) {
|
||||||
return m.ResetFunc(rs)
|
return m.ResetFunc(rs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +42,6 @@ func (m *MockRequestHandler) Shutdown() {
|
|||||||
m.ShutdownFunc()
|
m.ShutdownFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRequestHandler) GetRequestParser() request.RequestParser {
|
func (m *MockRequestHandler) GetRequestParser() handlers.RequestParser {
|
||||||
return m.GetRequestParserFunc()
|
return m.GetRequestParserFunc()
|
||||||
}
|
}
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
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(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,
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user