273 lines
9.2 KiB
Go
273 lines
9.2 KiB
Go
package application
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.defalsify.org/vise.git/db"
|
|
"git.defalsify.org/vise.git/resource"
|
|
"git.grassecon.net/grassrootseconomics/sarafu-vise/store"
|
|
storedb "git.grassecon.net/grassrootseconomics/sarafu-vise/store/db"
|
|
dataserviceapi "github.com/grassrootseconomics/ussd-data-service/pkg/api"
|
|
"gopkg.in/leonelquinteros/gotext.v1"
|
|
)
|
|
|
|
// ManageVouchers retrieves the token holdings from the API using the "PublicKey" and
|
|
// 1. sets the first as the default voucher if no active voucher is set.
|
|
// 2. Stores list of vouchers
|
|
// 3. updates the balance of the active voucher
|
|
func (h *MenuHandlers) ManageVouchers(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
var res resource.Result
|
|
userStore := h.userdataStore
|
|
logdb := h.logDb
|
|
|
|
sessionId, ok := ctx.Value("SessionId").(string)
|
|
if !ok {
|
|
return res, fmt.Errorf("missing session")
|
|
}
|
|
|
|
flag_no_active_voucher, _ := h.flagManager.GetFlag("flag_no_active_voucher")
|
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
|
|
|
publicKey, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_PUBLIC_KEY)
|
|
if err != nil {
|
|
logg.ErrorCtxf(ctx, "failed to read publicKey entry", "key", storedb.DATA_PUBLIC_KEY, "error", err)
|
|
return res, err
|
|
}
|
|
|
|
// Fetch vouchers from API
|
|
vouchersResp, err := h.accountService.FetchVouchers(ctx, string(publicKey))
|
|
if err != nil {
|
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
|
return res, nil
|
|
}
|
|
res.FlagReset = append(res.FlagReset, flag_api_error)
|
|
|
|
if len(vouchersResp) == 0 {
|
|
res.FlagSet = append(res.FlagSet, flag_no_active_voucher)
|
|
return res, nil
|
|
}
|
|
|
|
res.FlagReset = append(res.FlagReset, flag_no_active_voucher)
|
|
|
|
// Check if user has an active voucher with proper error handling
|
|
activeSym, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_SYM)
|
|
if err != nil {
|
|
if db.IsNotFound(err) {
|
|
// No active voucher, set the first one as default
|
|
firstVoucher := vouchersResp[0]
|
|
defaultSym := firstVoucher.TokenSymbol
|
|
defaultBal := firstVoucher.Balance
|
|
defaultDec := firstVoucher.TokenDecimals
|
|
defaultAddr := firstVoucher.TokenAddress
|
|
|
|
// Scale down the balance
|
|
scaledBalance := store.ScaleDownBalance(defaultBal, defaultDec)
|
|
|
|
firstVoucherMap := map[storedb.DataTyp]string{
|
|
storedb.DATA_ACTIVE_SYM: defaultSym,
|
|
storedb.DATA_ACTIVE_BAL: scaledBalance,
|
|
storedb.DATA_ACTIVE_DECIMAL: defaultDec,
|
|
storedb.DATA_ACTIVE_ADDRESS: defaultAddr,
|
|
}
|
|
|
|
for key, value := range firstVoucherMap {
|
|
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
|
|
logg.ErrorCtxf(ctx, "Failed to write active voucher data", "key", key, "error", err)
|
|
return res, err
|
|
}
|
|
err = logdb.WriteLogEntry(ctx, sessionId, key, []byte(value))
|
|
if err != nil {
|
|
logg.DebugCtxf(ctx, "Failed to write voucher db log entry", "key", key, "value", value)
|
|
}
|
|
}
|
|
|
|
logg.InfoCtxf(ctx, "Default voucher set", "symbol", defaultSym, "balance", defaultBal, "decimals", defaultDec, "address", defaultAddr)
|
|
} else {
|
|
logg.ErrorCtxf(ctx, "failed to read activeSym entry with", "key", storedb.DATA_ACTIVE_SYM, "error", err)
|
|
return res, err
|
|
}
|
|
} else {
|
|
// Update active voucher balance
|
|
activeSymStr := string(activeSym)
|
|
|
|
// Find the matching voucher data
|
|
var activeData *dataserviceapi.TokenHoldings
|
|
for _, voucher := range vouchersResp {
|
|
if voucher.TokenSymbol == activeSymStr {
|
|
activeData = &voucher
|
|
break
|
|
}
|
|
}
|
|
|
|
if activeData == nil {
|
|
logg.ErrorCtxf(ctx, "activeSym not found in vouchers, setting the first voucher as the default", "activeSym", activeSymStr)
|
|
firstVoucher := vouchersResp[0]
|
|
activeData = &firstVoucher
|
|
}
|
|
|
|
// Scale down the balance
|
|
scaledBalance := store.ScaleDownBalance(activeData.Balance, activeData.TokenDecimals)
|
|
|
|
// Update the balance field with the scaled value
|
|
activeData.Balance = scaledBalance
|
|
|
|
// Pass the matching voucher data to UpdateVoucherData
|
|
if err := store.UpdateVoucherData(ctx, h.userdataStore, sessionId, activeData); err != nil {
|
|
logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err)
|
|
return res, err
|
|
}
|
|
}
|
|
|
|
// Store all voucher data
|
|
data := store.ProcessVouchers(vouchersResp)
|
|
dataMap := map[storedb.DataTyp]string{
|
|
storedb.DATA_VOUCHER_SYMBOLS: data.Symbols,
|
|
storedb.DATA_VOUCHER_BALANCES: data.Balances,
|
|
storedb.DATA_VOUCHER_DECIMALS: data.Decimals,
|
|
storedb.DATA_VOUCHER_ADDRESSES: data.Addresses,
|
|
}
|
|
|
|
// Write data entries
|
|
for key, value := range dataMap {
|
|
if err := userStore.WriteEntry(ctx, sessionId, key, []byte(value)); err != nil {
|
|
logg.ErrorCtxf(ctx, "Failed to write data entry for sessionId: %s", sessionId, "key", key, "error", err)
|
|
continue
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// GetVoucherList fetches the list of vouchers and formats them.
|
|
func (h *MenuHandlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
var res resource.Result
|
|
sessionId, ok := ctx.Value("SessionId").(string)
|
|
if !ok {
|
|
return res, fmt.Errorf("missing session")
|
|
}
|
|
|
|
userStore := h.userdataStore
|
|
|
|
// Read vouchers from the store
|
|
voucherData, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_SYMBOLS)
|
|
logg.InfoCtxf(ctx, "reading voucherData in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_SYMBOLS, "voucherData", voucherData)
|
|
if err != nil {
|
|
logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_SYMBOLS, "error", err)
|
|
return res, err
|
|
}
|
|
|
|
voucherBalances, err := userStore.ReadEntry(ctx, sessionId, storedb.DATA_VOUCHER_BALANCES)
|
|
logg.InfoCtxf(ctx, "reading voucherBalances in GetVoucherList", "sessionId", sessionId, "key", storedb.DATA_VOUCHER_BALANCES, "voucherBalances", voucherBalances)
|
|
if err != nil {
|
|
logg.ErrorCtxf(ctx, "failed to read voucherData entires with", "key", storedb.DATA_VOUCHER_BALANCES, "error", err)
|
|
return res, err
|
|
}
|
|
|
|
formattedVoucherList := store.FormatVoucherList(ctx, string(voucherData), string(voucherBalances))
|
|
finalOutput := strings.Join(formattedVoucherList, "\n")
|
|
|
|
logg.InfoCtxf(ctx, "final output for GetVoucherList", "sessionId", sessionId, "finalOutput", finalOutput)
|
|
|
|
res.Content = finalOutput
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// ViewVoucher retrieves the token holding and balance from the subprefixDB
|
|
// and displays it to the user for them to select it.
|
|
func (h *MenuHandlers) ViewVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
var res resource.Result
|
|
sessionId, ok := ctx.Value("SessionId").(string)
|
|
if !ok {
|
|
return res, fmt.Errorf("missing session")
|
|
}
|
|
|
|
code := codeFromCtx(ctx)
|
|
l := gotext.NewLocale(translationDir, code)
|
|
l.AddDomain("default")
|
|
|
|
flag_incorrect_voucher, _ := h.flagManager.GetFlag("flag_incorrect_voucher")
|
|
|
|
inputStr := string(input)
|
|
|
|
metadata, err := store.GetVoucherData(ctx, h.userdataStore, sessionId, inputStr)
|
|
if err != nil {
|
|
return res, fmt.Errorf("failed to retrieve voucher data: %v", err)
|
|
}
|
|
|
|
if metadata == nil {
|
|
res.FlagSet = append(res.FlagSet, flag_incorrect_voucher)
|
|
return res, nil
|
|
}
|
|
|
|
if err := store.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
|
|
logg.ErrorCtxf(ctx, "failed on StoreTemporaryVoucher", "error", err)
|
|
return res, err
|
|
}
|
|
|
|
res.FlagReset = append(res.FlagReset, flag_incorrect_voucher)
|
|
res.Content = l.Get("Symbol: %s\nBalance: %s", metadata.TokenSymbol, metadata.Balance)
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// SetVoucher retrieves the temp voucher data and sets it as the active data.
|
|
func (h *MenuHandlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
var res resource.Result
|
|
|
|
sessionId, ok := ctx.Value("SessionId").(string)
|
|
if !ok {
|
|
return res, fmt.Errorf("missing session")
|
|
}
|
|
|
|
// Get temporary data
|
|
tempData, err := store.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
|
|
if err != nil {
|
|
logg.ErrorCtxf(ctx, "failed on GetTemporaryVoucherData", "error", err)
|
|
return res, err
|
|
}
|
|
|
|
// Set as active and clear temporary data
|
|
if err := store.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil {
|
|
logg.ErrorCtxf(ctx, "failed on UpdateVoucherData", "error", err)
|
|
return res, err
|
|
}
|
|
|
|
res.Content = tempData.TokenSymbol
|
|
return res, nil
|
|
}
|
|
|
|
// GetVoucherDetails retrieves the voucher details.
|
|
func (h *MenuHandlers) GetVoucherDetails(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
var res resource.Result
|
|
store := h.userdataStore
|
|
sessionId, ok := ctx.Value("SessionId").(string)
|
|
if !ok {
|
|
return res, fmt.Errorf("missing session")
|
|
}
|
|
flag_api_error, _ := h.flagManager.GetFlag("flag_api_call_error")
|
|
|
|
// get the active address
|
|
activeAddress, err := store.ReadEntry(ctx, sessionId, storedb.DATA_ACTIVE_ADDRESS)
|
|
if err != nil {
|
|
logg.ErrorCtxf(ctx, "failed to read activeAddress entry with", "key", storedb.DATA_ACTIVE_ADDRESS, "error", err)
|
|
return res, err
|
|
}
|
|
|
|
// use the voucher contract address to get the data from the API
|
|
voucherData, err := h.accountService.VoucherData(ctx, string(activeAddress))
|
|
if err != nil {
|
|
res.FlagSet = append(res.FlagSet, flag_api_error)
|
|
return res, nil
|
|
}
|
|
res.FlagReset = append(res.FlagReset, flag_api_error)
|
|
|
|
res.Content = fmt.Sprintf(
|
|
"Name: %s\nSymbol: %s\nCommodity: %s\nLocation: %s", voucherData.TokenName, voucherData.TokenSymbol, voucherData.TokenCommodity, voucherData.TokenLocation,
|
|
)
|
|
|
|
return res, nil
|
|
}
|