Skip to content

Commit 03b68ff

Browse files
ac4chcain4chainchris-4chain
authored
[FEATURE] CRUD for knownTx entity (#505)
Co-authored-by: cain4chain <[email protected]> Co-authored-by: chris-4chain <[email protected]>
1 parent 0a03943 commit 03b68ff

File tree

11 files changed

+641
-6
lines changed

11 files changed

+641
-6
lines changed

pkg/entity/comparable.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package entity
22

3-
import "github.com/go-softwarelab/common/pkg/types"
3+
import (
4+
"fmt"
5+
6+
"github.com/go-softwarelab/common/pkg/slices"
7+
"github.com/go-softwarelab/common/pkg/types"
8+
)
49

510
// CmpOperator defines an integer-based enumeration representing various comparison operators
611
type CmpOperator int
@@ -49,3 +54,44 @@ func (c *Comparable[T]) GetValueRight() T {
4954
func (c *Comparable[T]) GetInValues() []T {
5055
return c.InValues
5156
}
57+
58+
// ToStringComparable converts a Comparable of any comparable type to a Comparable of string type using fmt.Sprint for values.
59+
func (c *Comparable[T]) ToStringComparable() *Comparable[string] {
60+
return &Comparable[string]{
61+
Value: fmt.Sprint(c.Value),
62+
ValueRight: fmt.Sprint(c.ValueRight),
63+
InValues: slices.Map(c.InValues, func(v T) string { return fmt.Sprint(v) }),
64+
Cmp: c.Cmp,
65+
}
66+
}
67+
68+
func (op CmpOperator) String() string {
69+
switch op {
70+
case GreaterThan:
71+
return "gt"
72+
case LessThan:
73+
return "lt"
74+
case Equal:
75+
return "eq"
76+
case NotEqual:
77+
return "neq"
78+
case GreaterThanOrEqual:
79+
return "gte"
80+
case LessThanOrEqual:
81+
return "lte"
82+
case Between:
83+
return "between"
84+
case NotBetween:
85+
return "not_between"
86+
case Like:
87+
return "like"
88+
case NotLike:
89+
return "not_like"
90+
case In:
91+
return "in"
92+
case NotIn:
93+
return "not_in"
94+
default:
95+
return "unknown"
96+
}
97+
}

pkg/entity/known_tx.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,10 @@ type KnownTxReadSpecification struct {
4040
TxID *string
4141

4242
IncludeHistoryNotes bool
43+
Status *Comparable[wdk.ProvenTxReqStatus]
4344
Attempts *Comparable[uint64]
45+
Notified *Comparable[bool]
46+
BlockHeight *Comparable[uint32]
47+
MerkleRoot *Comparable[string]
48+
BlockHash *Comparable[string]
4449
}

pkg/internal/storage/repo/gen_cmp_condition.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func cmpCondition[T types.Ordered](fieldExpr fieldExpr[T], cmpExpr comparableExp
5959
case entity.NotIn:
6060
return fieldExpr.NotIn(cmpExpr.GetInValues()...)
6161
default:
62-
panic("unsupported comparison operator")
62+
panic("unsupported comparison operator " + cmp.String())
6363
}
6464
}
6565

@@ -69,3 +69,27 @@ func ordered[T types.Ordered](a, b T) (T, T) {
6969
}
7070
return a, b
7171
}
72+
73+
func cmpBoolCondition(field field.Bool, cmp *entity.Comparable[bool]) gen.Condition {
74+
switch cmp.Cmp {
75+
case entity.Equal:
76+
return field.Is(cmp.Value)
77+
case entity.NotEqual:
78+
return field.Is(!cmp.Value)
79+
80+
case entity.GreaterThan,
81+
entity.LessThan,
82+
entity.GreaterThanOrEqual,
83+
entity.LessThanOrEqual,
84+
entity.Between,
85+
entity.NotBetween,
86+
entity.Like,
87+
entity.NotLike,
88+
entity.In,
89+
entity.NotIn:
90+
panic("unsupported comparison operator for bool: " + cmp.Cmp.String())
91+
92+
default:
93+
panic("unknown comparison operator for bool: " + cmp.Cmp.String())
94+
}
95+
}

pkg/internal/storage/repo/known_tx.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ func (p *KnownTx) SetBatchForKnownTxs(ctx context.Context, txIDs []string, batch
348348

349349
func (p *KnownTx) conditionsBySpec(spec *pkgentity.KnownTxReadSpecification) []gen.Condition {
350350
if spec == nil {
351-
return []gen.Condition{}
351+
return nil
352352
}
353353

354354
table := &p.query.KnownTx
@@ -360,8 +360,21 @@ func (p *KnownTx) conditionsBySpec(spec *pkgentity.KnownTxReadSpecification) []g
360360
if spec.Attempts != nil {
361361
conditions = append(conditions, cmpCondition(table.Attempts, spec.Attempts))
362362
}
363-
364-
// TODO: Add more conditions based on the spec
363+
if spec.Status != nil {
364+
conditions = append(conditions, cmpCondition(table.Status, spec.Status.ToStringComparable()))
365+
}
366+
if spec.Notified != nil {
367+
conditions = append(conditions, cmpBoolCondition(table.Notified, spec.Notified))
368+
}
369+
if spec.BlockHeight != nil {
370+
conditions = append(conditions, cmpCondition(table.BlockHeight, spec.BlockHeight))
371+
}
372+
if spec.MerkleRoot != nil {
373+
conditions = append(conditions, cmpCondition(table.MerkleRoot, spec.MerkleRoot))
374+
}
375+
if spec.BlockHash != nil {
376+
conditions = append(conditions, cmpCondition(table.BlockHash, spec.BlockHash))
377+
}
365378

366379
return conditions
367380
}

pkg/internal/testabilities/testservices/fixture_arc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ type ARCQueryFixture interface {
5858
WillReturnWithMindedTx() ARCQueryFixture
5959
WillReturnTransactionOnHeight(i int)
6060
WillReturnTransactionWithBlockHash(hash *chainhash.Hash)
61+
WillReturnTransactionWithBlockHeight(height uint32)
6162
}
6263

6364
type ArcBroadcastFixture interface {
@@ -416,3 +417,9 @@ func errorResponseForStatusWithExtraInfo(httpStatus int, extraInfo string) (int,
416417
"type": "https://bitcoin-sv.github.io/arc/#/errors?id=_" + to.StringFromInteger(httpStatus),
417418
}
418419
}
420+
421+
func (a *arcQueryFixture) WillReturnTransactionWithBlockHeight(height uint32) {
422+
mp := testutils.MockValidMerklePath(a.TB, a.txID)
423+
mp.BlockHeight = height
424+
a.WillReturnTransactionWithMerklePath(mp)
425+
}

pkg/storage/crud/bool_condition.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package crud
2+
3+
import "github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"
4+
5+
// BoolCondition defines comparison operations for boolean values.
6+
// It enables method chaining for query filters involving bool fields.
7+
type BoolCondition[Parent any] interface {
8+
Equals(value bool) Parent
9+
NotEquals(value bool) Parent
10+
In(value ...bool) Parent
11+
NotIn(value ...bool) Parent
12+
}
13+
14+
type boolCondition[Parent any] struct {
15+
parent Parent
16+
conditionSetter func(spec *entity.Comparable[bool])
17+
}
18+
19+
func (c *boolCondition[Parent]) Equals(value bool) Parent {
20+
c.conditionSetter(&entity.Comparable[bool]{
21+
Value: value,
22+
Cmp: entity.Equal,
23+
})
24+
return c.parent
25+
}
26+
27+
func (c *boolCondition[Parent]) NotEquals(value bool) Parent {
28+
c.conditionSetter(&entity.Comparable[bool]{
29+
Value: value,
30+
Cmp: entity.NotEqual,
31+
})
32+
return c.parent
33+
}
34+
35+
func (c *boolCondition[Parent]) In(values ...bool) Parent {
36+
c.conditionSetter(&entity.Comparable[bool]{
37+
InValues: values,
38+
Cmp: entity.In,
39+
})
40+
return c.parent
41+
}
42+
43+
func (c *boolCondition[Parent]) NotIn(values ...bool) Parent {
44+
c.conditionSetter(&entity.Comparable[bool]{
45+
InValues: values,
46+
Cmp: entity.NotIn,
47+
})
48+
return c.parent
49+
}

pkg/storage/crud/enum_condition.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package crud
2+
3+
import "github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"
4+
5+
// StringEnumCondition defines composable filter operations for string-based enum fields in query building.
6+
// It provides equality, inequality and pattern-matching methods that return the Parent type for fluent chainability.
7+
// Methods enable use of equals, not-equals, like, not-like, in, and not-in filters for enum-typed columns.
8+
type StringEnumCondition[Parent any, EnumType ~string] interface {
9+
Equals(value EnumType) Parent
10+
NotEquals(value EnumType) Parent
11+
Like(value EnumType) Parent
12+
NotLike(value EnumType) Parent
13+
In(value ...EnumType) Parent
14+
NotIn(value ...EnumType) Parent
15+
}
16+
17+
type stringEnumCondition[Parent any, EnumType ~string] struct {
18+
parent Parent
19+
conditionSetter func(spec *entity.Comparable[EnumType])
20+
}
21+
22+
func (c *stringEnumCondition[Parent, EnumType]) Equals(value EnumType) Parent {
23+
c.conditionSetter(&entity.Comparable[EnumType]{
24+
Value: value,
25+
Cmp: entity.Equal,
26+
})
27+
28+
return c.parent
29+
}
30+
31+
func (c *stringEnumCondition[Parent, EnumType]) NotEquals(value EnumType) Parent {
32+
c.conditionSetter(&entity.Comparable[EnumType]{
33+
Value: value,
34+
Cmp: entity.NotEqual,
35+
})
36+
37+
return c.parent
38+
}
39+
40+
func (c *stringEnumCondition[Parent, EnumType]) Like(value EnumType) Parent {
41+
c.conditionSetter(&entity.Comparable[EnumType]{
42+
Value: value,
43+
Cmp: entity.Like,
44+
})
45+
46+
return c.parent
47+
}
48+
49+
func (c *stringEnumCondition[Parent, EnumType]) NotLike(value EnumType) Parent {
50+
c.conditionSetter(&entity.Comparable[EnumType]{
51+
Value: value,
52+
Cmp: entity.NotLike,
53+
})
54+
55+
return c.parent
56+
}
57+
58+
func (c *stringEnumCondition[Parent, EnumType]) In(value ...EnumType) Parent {
59+
c.conditionSetter(&entity.Comparable[EnumType]{
60+
InValues: value,
61+
Cmp: entity.In,
62+
})
63+
64+
return c.parent
65+
}
66+
67+
func (c *stringEnumCondition[Parent, EnumType]) NotIn(values ...EnumType) Parent {
68+
c.conditionSetter(&entity.Comparable[EnumType]{
69+
InValues: values,
70+
Cmp: entity.NotIn,
71+
})
72+
73+
return c.parent
74+
}

pkg/storage/crud/known_tx.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/entity"
99
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/internal/storage/queryopts"
10+
"github.com/bsv-blockchain/go-wallet-toolbox/pkg/wdk"
1011
"github.com/go-softwarelab/common/pkg/to"
1112
)
1213

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

3031
TxID(txID string) KnownTxReadOperations
3132
Attempts() NumericCondition[KnownTxReader, uint64]
33+
Status() StringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus]
34+
Notified() BoolCondition[KnownTxReader]
35+
BlockHeight() NumericCondition[KnownTxReader, uint32]
36+
MerkleRoot() StringCondition[KnownTxReader]
37+
BlockHash() StringCondition[KnownTxReader]
38+
3239
Since(value time.Time, column entity.SinceField) KnownTxReader
3340
Paged(limit, offset int, desc bool) KnownTxReader
3441
}
@@ -108,3 +115,48 @@ func (k *knownTx) Attempts() NumericCondition[KnownTxReader, uint64] {
108115
},
109116
}
110117
}
118+
119+
func (k *knownTx) Status() StringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus] {
120+
return &stringEnumCondition[KnownTxReader, wdk.ProvenTxReqStatus]{
121+
parent: k,
122+
conditionSetter: func(spec *entity.Comparable[wdk.ProvenTxReqStatus]) {
123+
k.spec.Status = spec
124+
},
125+
}
126+
}
127+
128+
func (k *knownTx) Notified() BoolCondition[KnownTxReader] {
129+
return &boolCondition[KnownTxReader]{
130+
parent: k,
131+
conditionSetter: func(spec *entity.Comparable[bool]) {
132+
k.spec.Notified = spec
133+
},
134+
}
135+
}
136+
137+
func (k *knownTx) BlockHeight() NumericCondition[KnownTxReader, uint32] {
138+
return &numericCondition[KnownTxReader, uint32]{
139+
parent: k,
140+
conditionSetter: func(spec *entity.Comparable[uint32]) {
141+
k.spec.BlockHeight = spec
142+
},
143+
}
144+
}
145+
146+
func (k *knownTx) MerkleRoot() StringCondition[KnownTxReader] {
147+
return &stringCondition[KnownTxReader]{
148+
parent: k,
149+
conditionSetter: func(spec *entity.Comparable[string]) {
150+
k.spec.MerkleRoot = spec
151+
},
152+
}
153+
}
154+
155+
func (k *knownTx) BlockHash() StringCondition[KnownTxReader] {
156+
return &stringCondition[KnownTxReader]{
157+
parent: k,
158+
conditionSetter: func(spec *entity.Comparable[string]) {
159+
k.spec.BlockHash = spec
160+
},
161+
}
162+
}

pkg/storage/internal/testabilities/assertions_db_state.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,15 @@ type DBStateAssertion interface {
5555

5656
type KnownTxAssertion interface {
5757
WithStatus(state wdk.ProvenTxReqStatus) KnownTxAssertion
58+
WithAttempts(attempts uint64) KnownTxAssertion
5859
IsMined() KnownTxAssertion
5960
NotMined() KnownTxAssertion
6061
HasRawTx() KnownTxAssertion
62+
IsNotified(expected bool) KnownTxAssertion
63+
WithBlockHeight(expected *uint32) KnownTxAssertion
64+
WithMerkleRoot(expected *string) KnownTxAssertion
65+
WithBlockHash(expected *string) KnownTxAssertion
6166
TxNotes(assertion func(TxNotesAssertion)) KnownTxAssertion
62-
WithAttempts(attempts uint64) KnownTxAssertion
6367
}
6468

6569
type UserTransactionAssertion interface {
@@ -214,6 +218,30 @@ func (d *knownTxAssertion) WithAttempts(expected uint64) KnownTxAssertion {
214218
return d
215219
}
216220

221+
func (d *knownTxAssertion) WithBlockHeight(expected *uint32) KnownTxAssertion {
222+
d.Helper()
223+
assert.Equal(d, expected, d.knownTx.BlockHeight, "Expected known tx to have BlockHeight = %v", expected)
224+
return d
225+
}
226+
227+
func (d *knownTxAssertion) WithMerkleRoot(expected *string) KnownTxAssertion {
228+
d.Helper()
229+
assert.Equal(d, expected, d.knownTx.MerkleRoot, "Expected MerkleRoot = %v", expected)
230+
return d
231+
}
232+
233+
func (d *knownTxAssertion) WithBlockHash(expected *string) KnownTxAssertion {
234+
d.Helper()
235+
assert.Equal(d, expected, d.knownTx.BlockHash, "Expected BlockHash = %v", expected)
236+
return d
237+
}
238+
239+
func (d *knownTxAssertion) IsNotified(expected bool) KnownTxAssertion {
240+
d.Helper()
241+
assert.Equal(d, expected, d.knownTx.Notified, "Expected known transaction to have Notified = %v", expected)
242+
return d
243+
}
244+
217245
type txNotesAssertion struct {
218246
testing.TB
219247
txNotes []*pkgentity.TxHistoryNote

0 commit comments

Comments
 (0)