voucher-data #138

Merged
lash merged 18 commits from voucher-data into master 2024-10-31 13:44:19 +01:00
2 changed files with 191 additions and 172 deletions
Showing only changes of commit 0480c02633 - Show all commits

View File

@ -1118,7 +1118,7 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
return res, nil return res, nil
} }
data := processVouchers(vouchersResp.Result.Holdings) data := utils.ProcessVouchers(vouchersResp.Result.Holdings)
// Store all voucher data // Store all voucher data
dataMap := map[string]string{ dataMap := map[string]string{
@ -1137,31 +1137,6 @@ func (h *Handlers) CheckVouchers(ctx context.Context, sym string, input []byte)
return res, nil return res, nil
} }
// processVouchers converts holdings into formatted strings
func processVouchers(holdings []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
}) VoucherMetadata {
var data VoucherMetadata
var symbols, balances, decimals, addresses []string
for i, h := range holdings {
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
}
data.Symbol = strings.Join(symbols, "\n")
data.Balance = strings.Join(balances, "\n")
data.Decimal = strings.Join(decimals, "\n")
data.Address = strings.Join(addresses, "\n")
return data
}
// GetVoucherList fetches the list of vouchers and formats them // GetVoucherList fetches the list of vouchers and formats them
func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) GetVoucherList(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
Alfred-mk marked this conversation as resolved Outdated
Outdated
Review

this is now the same struct as VoucherMetadata can we consolidate please?

this is now the same struct as `VoucherMetadata` can we consolidate please?
@ -1193,8 +1168,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
return res, nil return res, nil
} }
// Retrieve the voucher metadata using the PrefixDb interface metadata, err := utils.GetVoucherData(ctx, h.prefixDb, inputStr)
metadata, err := getVoucherData(ctx, h.prefixDb, inputStr)
if err != nil { if err != nil {
return res, fmt.Errorf("failed to retrieve voucher data: %v", err) return res, fmt.Errorf("failed to retrieve voucher data: %v", err)
} }
@ -1204,7 +1178,7 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
return res, nil return res, nil
} }
if err := h.storeTemporaryVoucher(ctx, sessionId, metadata); err != nil { if err := utils.StoreTemporaryVoucher(ctx, h.userdataStore, sessionId, metadata); err != nil {
return res, err return res, err
} }
@ -1214,86 +1188,9 @@ func (h *Handlers) ViewVoucher(ctx context.Context, sym string, input []byte) (r
return res, nil return res, nil
} }
// getVoucherData retrieves and matches voucher data // SetVoucher retrieves the temp voucher data and sets it as the active data
func getVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) {
keys := []string{"sym", "bal", "deci", "addr"}
data := make(map[string]string)
for _, key := range keys {
value, err := db.Get(ctx, []byte(key))
if err != nil {
return nil, fmt.Errorf("failed to get %s: %v", key, err)
}
data[key] = string(value)
}
symbol, balance, decimal, address := matchVoucher(input,
data["sym"],
data["bal"],
data["deci"],
data["addr"])
if symbol == "" {
return nil, nil
}
return &VoucherMetadata{
Symbol: symbol,
Balance: balance,
Decimal: decimal,
Address: address,
}, nil
}
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
func matchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
symList := strings.Split(symbols, "\n")
balList := strings.Split(balances, "\n")
decList := strings.Split(decimals, "\n")
addrList := strings.Split(addresses, "\n")
for i, sym := range symList {
parts := strings.SplitN(sym, ":", 2)
if len(parts) != 2 {
continue
}
if input == parts[0] || strings.EqualFold(input, parts[1]) {
symbol = parts[1]
if i < len(balList) {
balance = strings.SplitN(balList[i], ":", 2)[1]
}
if i < len(decList) {
decimal = strings.SplitN(decList[i], ":", 2)[1]
}
if i < len(addrList) {
address = strings.SplitN(addrList[i], ":", 2)[1]
}
break
}
}
return
}
func (h *Handlers) storeTemporaryVoucher(ctx context.Context, sessionId string, data *VoucherMetadata) error {
entries := map[utils.DataTyp][]byte{
utils.DATA_TEMPORARY_SYM: []byte(data.Symbol),
utils.DATA_TEMPORARY_BAL: []byte(data.Balance),
utils.DATA_TEMPORARY_DECIMAL: []byte(data.Decimal),
utils.DATA_TEMPORARY_ADDRESS: []byte(data.Address),
}
for key, value := range entries {
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}
func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) { func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (resource.Result, error) {
var res resource.Result var res resource.Result
var err error
sessionId, ok := ctx.Value("SessionId").(string) sessionId, ok := ctx.Value("SessionId").(string)
if !ok { if !ok {
@ -1301,78 +1198,16 @@ func (h *Handlers) SetVoucher(ctx context.Context, sym string, input []byte) (re
} }
// Get temporary data // Get temporary data
tempData, err := h.getTemporaryVoucherData(ctx, sessionId) tempData, err := utils.GetTemporaryVoucherData(ctx, h.userdataStore, sessionId)
if err != nil { if err != nil {
return res, err return res, err
} }
// Set as active and clear temporary // Set as active and clear temporary data
if err := h.updateVoucherData(ctx, sessionId, tempData); err != nil { if err := utils.UpdateVoucherData(ctx, h.userdataStore, sessionId, tempData); err != nil {
return res, err return res, err
} }
res.Content = tempData.Symbol res.Content = tempData.Symbol
return res, nil return res, nil
} }
func (h *Handlers) getTemporaryVoucherData(ctx context.Context, sessionId string) (*VoucherMetadata, error) {
store := h.userdataStore
keys := []utils.DataTyp{
utils.DATA_TEMPORARY_SYM,
utils.DATA_TEMPORARY_BAL,
utils.DATA_TEMPORARY_DECIMAL,
utils.DATA_TEMPORARY_ADDRESS,
}
data := &VoucherMetadata{}
values := make([][]byte, len(keys))
for i, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, err
}
values[i] = value
}
data.Symbol = string(values[0])
data.Balance = string(values[1])
data.Decimal = string(values[2])
data.Address = string(values[3])
return data, nil
}
func (h *Handlers) updateVoucherData(ctx context.Context, sessionId string, data *VoucherMetadata) error {
// Set active voucher data
activeEntries := map[utils.DataTyp][]byte{
utils.DATA_ACTIVE_SYM: []byte(data.Symbol),
utils.DATA_ACTIVE_BAL: []byte(data.Balance),
utils.DATA_ACTIVE_DECIMAL: []byte(data.Decimal),
utils.DATA_ACTIVE_ADDRESS: []byte(data.Address),
}
// Clear temporary voucher data
tempEntries := map[utils.DataTyp][]byte{
utils.DATA_TEMPORARY_SYM: []byte(""),
utils.DATA_TEMPORARY_BAL: []byte(""),
utils.DATA_TEMPORARY_DECIMAL: []byte(""),
utils.DATA_TEMPORARY_ADDRESS: []byte(""),
}
// Write all entries
for key, value := range activeEntries {
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
for key, value := range tempEntries {
if err := h.userdataStore.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}

184
internal/utils/vouchers.go Normal file
View File

@ -0,0 +1,184 @@
package utils
import (
"fmt"
"strings"
"context"
"git.grassecon.net/urdt/ussd/internal/storage"
)
// VoucherMetadata helps organize voucher data fields
type VoucherMetadata struct {
Symbol string
Balance string
Decimal string
Address string
}
// ProcessVouchers converts holdings into formatted strings
func ProcessVouchers(holdings []struct {
ContractAddress string `json:"contractAddress"`
TokenSymbol string `json:"tokenSymbol"`
TokenDecimals string `json:"tokenDecimals"`
Balance string `json:"balance"`
}) VoucherMetadata {
var data VoucherMetadata
var symbols, balances, decimals, addresses []string
for i, h := range holdings {
symbols = append(symbols, fmt.Sprintf("%d:%s", i+1, h.TokenSymbol))
balances = append(balances, fmt.Sprintf("%d:%s", i+1, h.Balance))
decimals = append(decimals, fmt.Sprintf("%d:%s", i+1, h.TokenDecimals))
addresses = append(addresses, fmt.Sprintf("%d:%s", i+1, h.ContractAddress))
}
data.Symbol = strings.Join(symbols, "\n")
data.Balance = strings.Join(balances, "\n")
data.Decimal = strings.Join(decimals, "\n")
data.Address = strings.Join(addresses, "\n")
return data
}
// GetVoucherData retrieves and matches voucher data
func GetVoucherData(ctx context.Context, db storage.PrefixDb, input string) (*VoucherMetadata, error) {
keys := []string{"sym", "bal", "deci", "addr"}
data := make(map[string]string)
for _, key := range keys {
value, err := db.Get(ctx, []byte(key))
if err != nil {
return nil, fmt.Errorf("failed to get %s: %v", key, err)
}
data[key] = string(value)
}
symbol, balance, decimal, address := MatchVoucher(input,
data["sym"],
data["bal"],
data["deci"],
data["addr"])
if symbol == "" {
return nil, nil
}
return &VoucherMetadata{
Symbol: symbol,
Balance: balance,
Decimal: decimal,
Address: address,
}, nil
}
// MatchVoucher finds the matching voucher symbol, balance, decimals and contract address based on the input.
func MatchVoucher(input, symbols, balances, decimals, addresses string) (symbol, balance, decimal, address string) {
symList := strings.Split(symbols, "\n")
balList := strings.Split(balances, "\n")
decList := strings.Split(decimals, "\n")
Alfred-mk marked this conversation as resolved Outdated
Outdated
Review

When will this condition occur?

When will this condition occur?

It will only occur if the string is not well formatted in the "1:SRF" format, such as it being "1SRF" or "1 SRF"

Ideally, this will not be encountered but I added this as an edge case in the unlikely event that it does

It will only occur if the string is not well formatted in the "1:SRF" format, such as it being "1SRF" or "1 SRF" Ideally, this will not be encountered but I added this as an edge case in the unlikely event that it does
Outdated
Review

but shouldnt that raise an error, as it should never happen?

but shouldnt that raise an error, as it should never happen?

On further assessment, I see it best to remove the code as the condition would never occur.

The data being matched comes from the db, and for this condition to occur means an issue lies with the functions that process and store the data

On further assessment, I see it best to remove the code as the condition would never occur. The data being matched comes from the db, and for this condition to occur means an issue lies with the functions that process and store the data
addrList := strings.Split(addresses, "\n")
for i, sym := range symList {
parts := strings.SplitN(sym, ":", 2)
if len(parts) != 2 {
continue
}
if input == parts[0] || strings.EqualFold(input, parts[1]) {
symbol = parts[1]
if i < len(balList) {
balance = strings.SplitN(balList[i], ":", 2)[1]
}
if i < len(decList) {
decimal = strings.SplitN(decList[i], ":", 2)[1]
}
if i < len(addrList) {
address = strings.SplitN(addrList[i], ":", 2)[1]
}
break
}
}
return
}
// StoreTemporaryVoucher saves voucher metadata as temporary entries in the DataStore.
func StoreTemporaryVoucher(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error {
entries := map[DataTyp][]byte{
DATA_TEMPORARY_SYM: []byte(data.Symbol),
DATA_TEMPORARY_BAL: []byte(data.Balance),
DATA_TEMPORARY_DECIMAL: []byte(data.Decimal),
DATA_TEMPORARY_ADDRESS: []byte(data.Address),
}
for key, value := range entries {
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}
// GetTemporaryVoucherData retrieves temporary voucher metadata from the DataStore.
func GetTemporaryVoucherData(ctx context.Context, store DataStore, sessionId string) (*VoucherMetadata, error) {
keys := []DataTyp{
DATA_TEMPORARY_SYM,
DATA_TEMPORARY_BAL,
DATA_TEMPORARY_DECIMAL,
DATA_TEMPORARY_ADDRESS,
}
data := &VoucherMetadata{}
values := make([][]byte, len(keys))
for i, key := range keys {
value, err := store.ReadEntry(ctx, sessionId, key)
if err != nil {
return nil, err
}
values[i] = value
}
data.Symbol = string(values[0])
data.Balance = string(values[1])
data.Decimal = string(values[2])
data.Address = string(values[3])
return data, nil
}
// UpdateVoucherData sets the active voucher data and clears the temporary voucher data in the DataStore.
func UpdateVoucherData(ctx context.Context, store DataStore, sessionId string, data *VoucherMetadata) error {
// Active voucher data entries
activeEntries := map[DataTyp][]byte{
DATA_ACTIVE_SYM: []byte(data.Symbol),
DATA_ACTIVE_BAL: []byte(data.Balance),
DATA_ACTIVE_DECIMAL: []byte(data.Decimal),
Alfred-mk marked this conversation as resolved
Review

Do we need to clear them here? Can't we just overwrite later?

Do we need to clear them here? Can't we just overwrite later?
Review

They can be left and overwritten with any new temporary data

I wanted to work on this functionality on a different PR, where we'll make use of a single temporary data row

They can be left and overwritten with any new temporary data I wanted to work on this functionality on a different PR, where we'll make use of a single temporary data row
Review

ok just drop the lines then?

ok just drop the lines then?
DATA_ACTIVE_ADDRESS: []byte(data.Address),
}
// Clear temporary voucher data entries
tempEntries := map[DataTyp][]byte{
DATA_TEMPORARY_SYM: []byte(""),
DATA_TEMPORARY_BAL: []byte(""),
DATA_TEMPORARY_DECIMAL: []byte(""),
DATA_TEMPORARY_ADDRESS: []byte(""),
}
// Write active data
for key, value := range activeEntries {
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
// Clear temporary data
for key, value := range tempEntries {
if err := store.WriteEntry(ctx, sessionId, key, value); err != nil {
return err
}
}
return nil
}