Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion pkg/entity/comparable.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package entity

import "github.com/go-softwarelab/common/pkg/types"
import (
"fmt"

"github.com/go-softwarelab/common/pkg/slices"
"github.com/go-softwarelab/common/pkg/types"
)

// CmpOperator defines an integer-based enumeration representing various comparison operators
type CmpOperator int
Expand Down Expand Up @@ -49,3 +54,44 @@ func (c *Comparable[T]) GetValueRight() T {
func (c *Comparable[T]) GetInValues() []T {
return c.InValues
}

// ToStringComparable converts a Comparable of any comparable type to a Comparable of string type using fmt.Sprint for values.
func (c *Comparable[T]) ToStringComparable() *Comparable[string] {
return &Comparable[string]{
Value: fmt.Sprint(c.Value),
ValueRight: fmt.Sprint(c.ValueRight),
InValues: slices.Map(c.InValues, func(v T) string { return fmt.Sprint(v) }),
Cmp: c.Cmp,
}
}

func (op CmpOperator) String() string {
switch op {
case GreaterThan:
return "gt"
case LessThan:
return "lt"
case Equal:
return "eq"
case NotEqual:
return "neq"
case GreaterThanOrEqual:
return "gte"
case LessThanOrEqual:
return "lte"
case Between:
return "between"
case NotBetween:
return "not_between"
case Like:
return "like"
case NotLike:
return "not_like"
case In:
return "in"
case NotIn:
return "not_in"
default:
return "unknown"
}
}
5 changes: 5 additions & 0 deletions pkg/entity/known_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ type KnownTxReadSpecification struct {
TxID *string

IncludeHistoryNotes bool
Status *Comparable[wdk.ProvenTxReqStatus]
Attempts *Comparable[uint64]
Notified *Comparable[bool]
BlockHeight *Comparable[uint32]
MerkleRoot *Comparable[string]
BlockHash *Comparable[string]
}
26 changes: 25 additions & 1 deletion pkg/internal/storage/repo/gen_cmp_condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func cmpCondition[T types.Ordered](fieldExpr fieldExpr[T], cmpExpr comparableExp
case entity.NotIn:
return fieldExpr.NotIn(cmpExpr.GetInValues()...)
default:
panic("unsupported comparison operator")
panic("unsupported comparison operator " + cmp.String())
}
}

Expand All @@ -69,3 +69,27 @@ func ordered[T types.Ordered](a, b T) (T, T) {
}
return a, b
}

func cmpBoolCondition(field field.Bool, cmp *entity.Comparable[bool]) gen.Condition {
switch cmp.Cmp {
case entity.Equal:
return field.Is(cmp.Value)
case entity.NotEqual:
return field.Is(!cmp.Value)

case entity.GreaterThan,
entity.LessThan,
entity.GreaterThanOrEqual,
entity.LessThanOrEqual,
entity.Between,
entity.NotBetween,
entity.Like,
entity.NotLike,
entity.In,
entity.NotIn:
panic("unsupported comparison operator for bool: " + cmp.Cmp.String())

default:
panic("unknown comparison operator for bool: " + cmp.Cmp.String())
}
}
19 changes: 16 additions & 3 deletions pkg/internal/storage/repo/known_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func (p *KnownTx) SetBatchForKnownTxs(ctx context.Context, txIDs []string, batch

func (p *KnownTx) conditionsBySpec(spec *pkgentity.KnownTxReadSpecification) []gen.Condition {
if spec == nil {
return []gen.Condition{}
return nil
}

table := &p.query.KnownTx
Expand All @@ -360,8 +360,21 @@ func (p *KnownTx) conditionsBySpec(spec *pkgentity.KnownTxReadSpecification) []g
if spec.Attempts != nil {
conditions = append(conditions, cmpCondition(table.Attempts, spec.Attempts))
}

// TODO: Add more conditions based on the spec
if spec.Status != nil {
conditions = append(conditions, cmpCondition(table.Status, spec.Status.ToStringComparable()))
}
if spec.Notified != nil {
conditions = append(conditions, cmpBoolCondition(table.Notified, spec.Notified))
}
if spec.BlockHeight != nil {
conditions = append(conditions, cmpCondition(table.BlockHeight, spec.BlockHeight))
}
if spec.MerkleRoot != nil {
conditions = append(conditions, cmpCondition(table.MerkleRoot, spec.MerkleRoot))
}
if spec.BlockHash != nil {
conditions = append(conditions, cmpCondition(table.BlockHash, spec.BlockHash))
}

return conditions
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/internal/testabilities/testservices/fixture_arc.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type ARCQueryFixture interface {
WillReturnWithMindedTx() ARCQueryFixture
WillReturnTransactionOnHeight(i int)
WillReturnTransactionWithBlockHash(hash *chainhash.Hash)
WillReturnTransactionWithBlockHeight(height uint32)
}

type ArcBroadcastFixture interface {
Expand Down Expand Up @@ -416,3 +417,9 @@ func errorResponseForStatusWithExtraInfo(httpStatus int, extraInfo string) (int,
"type": "https://bitcoin-sv.github.io/arc/#/errors?id=_" + to.StringFromInteger(httpStatus),
}
}

func (a *arcQueryFixture) WillReturnTransactionWithBlockHeight(height uint32) {
mp := testutils.MockValidMerklePath(a.TB, a.txID)
mp.BlockHeight = height
a.WillReturnTransactionWithMerklePath(mp)
}
49 changes: 49 additions & 0 deletions pkg/storage/crud/bool_condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package crud

import "github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"

// BoolCondition defines comparison operations for boolean values.
// It enables method chaining for query filters involving bool fields.
type BoolCondition[Parent any] interface {
Equals(value bool) Parent
NotEquals(value bool) Parent
In(value ...bool) Parent
NotIn(value ...bool) Parent
}

type boolCondition[Parent any] struct {
parent Parent
conditionSetter func(spec *entity.Comparable[bool])
}

func (c *boolCondition[Parent]) Equals(value bool) Parent {
c.conditionSetter(&entity.Comparable[bool]{
Value: value,
Cmp: entity.Equal,
})
return c.parent
}

func (c *boolCondition[Parent]) NotEquals(value bool) Parent {
c.conditionSetter(&entity.Comparable[bool]{
Value: value,
Cmp: entity.NotEqual,
})
return c.parent
}

func (c *boolCondition[Parent]) In(values ...bool) Parent {
c.conditionSetter(&entity.Comparable[bool]{
InValues: values,
Cmp: entity.In,
})
return c.parent
}

func (c *boolCondition[Parent]) NotIn(values ...bool) Parent {
c.conditionSetter(&entity.Comparable[bool]{
InValues: values,
Cmp: entity.NotIn,
})
return c.parent
}
74 changes: 74 additions & 0 deletions pkg/storage/crud/enum_condition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package crud

import "github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"

// StringEnumCondition defines composable filter operations for string-based enum fields in query building.
// It provides equality, inequality and pattern-matching methods that return the Parent type for fluent chainability.
// Methods enable use of equals, not-equals, like, not-like, in, and not-in filters for enum-typed columns.
type StringEnumCondition[Parent any, EnumType ~string] interface {
Equals(value EnumType) Parent
NotEquals(value EnumType) Parent
Like(value EnumType) Parent
NotLike(value EnumType) Parent
In(value ...EnumType) Parent
NotIn(value ...EnumType) Parent
}

type stringEnumCondition[Parent any, EnumType ~string] struct {
parent Parent
conditionSetter func(spec *entity.Comparable[EnumType])
}

func (c *stringEnumCondition[Parent, EnumType]) Equals(value EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
Value: value,
Cmp: entity.Equal,
})

return c.parent
}

func (c *stringEnumCondition[Parent, EnumType]) NotEquals(value EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
Value: value,
Cmp: entity.NotEqual,
})

return c.parent
}

func (c *stringEnumCondition[Parent, EnumType]) Like(value EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
Value: value,
Cmp: entity.Like,
})

return c.parent
}

func (c *stringEnumCondition[Parent, EnumType]) NotLike(value EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
Value: value,
Cmp: entity.NotLike,
})

return c.parent
}

func (c *stringEnumCondition[Parent, EnumType]) In(value ...EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
InValues: value,
Cmp: entity.In,
})

return c.parent
}

func (c *stringEnumCondition[Parent, EnumType]) NotIn(values ...EnumType) Parent {
c.conditionSetter(&entity.Comparable[EnumType]{
InValues: values,
Cmp: entity.NotIn,
})

return c.parent
}
52 changes: 52 additions & 0 deletions pkg/storage/crud/known_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/internal/storage/queryopts"
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/wdk"
"github.com/go-softwarelab/common/pkg/to"
)

Expand All @@ -29,6 +30,12 @@ type KnownTxReader interface {

TxID(txID string) KnownTxReadOperations
Attempts() NumericCondition[KnownTxReader, uint64]
Status() StringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus]
Notified() BoolCondition[KnownTxReader]
BlockHeight() NumericCondition[KnownTxReader, uint32]
MerkleRoot() StringCondition[KnownTxReader]
BlockHash() StringCondition[KnownTxReader]

Since(value time.Time, column entity.SinceField) KnownTxReader
Paged(limit, offset int, desc bool) KnownTxReader
}
Expand Down Expand Up @@ -108,3 +115,48 @@ func (k *knownTx) Attempts() NumericCondition[KnownTxReader, uint64] {
},
}
}

func (k *knownTx) Status() StringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus] {
return &stringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus]{
parent: k,
conditionSetter: func(spec *entity.Comparable[wdk.ProvenTxReqStatus]) {
k.spec.Status = spec
},
}
}

func (k *knownTx) Notified() BoolCondition[KnownTxReader] {
return &boolCondition[KnownTxReader]{
parent: k,
conditionSetter: func(spec *entity.Comparable[bool]) {
k.spec.Notified = spec
},
}
}

func (k *knownTx) BlockHeight() NumericCondition[KnownTxReader, uint32] {
return &numericCondition[KnownTxReader, uint32]{
parent: k,
conditionSetter: func(spec *entity.Comparable[uint32]) {
k.spec.BlockHeight = spec
},
}
}

func (k *knownTx) MerkleRoot() StringCondition[KnownTxReader] {
return &stringCondition[KnownTxReader]{
parent: k,
conditionSetter: func(spec *entity.Comparable[string]) {
k.spec.MerkleRoot = spec
},
}
}

func (k *knownTx) BlockHash() StringCondition[KnownTxReader] {
return &stringCondition[KnownTxReader]{
parent: k,
conditionSetter: func(spec *entity.Comparable[string]) {
k.spec.BlockHash = spec
},
}
}
30 changes: 29 additions & 1 deletion pkg/storage/internal/testabilities/assertions_db_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,15 @@ type DBStateAssertion interface {

type KnownTxAssertion interface {
WithStatus(state wdk.ProvenTxReqStatus) KnownTxAssertion
WithAttempts(attempts uint64) KnownTxAssertion
IsMined() KnownTxAssertion
NotMined() KnownTxAssertion
HasRawTx() KnownTxAssertion
IsNotified(expected bool) KnownTxAssertion
WithBlockHeight(expected *uint32) KnownTxAssertion
WithMerkleRoot(expected *string) KnownTxAssertion
WithBlockHash(expected *string) KnownTxAssertion
TxNotes(assertion func(TxNotesAssertion)) KnownTxAssertion
WithAttempts(attempts uint64) KnownTxAssertion
}

type UserTransactionAssertion interface {
Expand Down Expand Up @@ -214,6 +218,30 @@ func (d *knownTxAssertion) WithAttempts(expected uint64) KnownTxAssertion {
return d
}

func (d *knownTxAssertion) WithBlockHeight(expected *uint32) KnownTxAssertion {
d.Helper()
assert.Equal(d, expected, d.knownTx.BlockHeight, "Expected known tx to have BlockHeight = %v", expected)
return d
}

func (d *knownTxAssertion) WithMerkleRoot(expected *string) KnownTxAssertion {
d.Helper()
assert.Equal(d, expected, d.knownTx.MerkleRoot, "Expected MerkleRoot = %v", expected)
return d
}

func (d *knownTxAssertion) WithBlockHash(expected *string) KnownTxAssertion {
d.Helper()
assert.Equal(d, expected, d.knownTx.BlockHash, "Expected BlockHash = %v", expected)
return d
}

func (d *knownTxAssertion) IsNotified(expected bool) KnownTxAssertion {
d.Helper()
assert.Equal(d, expected, d.knownTx.Notified, "Expected known transaction to have Notified = %v", expected)
return d
}

type txNotesAssertion struct {
testing.TB
txNotes []*pkgentity.TxHistoryNote
Expand Down
Loading
Loading