firewalldb: introduce AddActionReq

Instead of passing an `Action` to the AddAction method, we introduce an
`AddActionReq` type which only holds the fields that are needed to
create a new Action. The rest of the info is determined by the DB layer.
This commit is contained in:
Elle Mouton 2025-05-12 13:41:48 +02:00
parent 297313e9c1
commit 8f7312f53a
No known key found for this signature in database
GPG key ID: D7D916376026F177
5 changed files with 61 additions and 37 deletions

View file

@ -5,7 +5,6 @@ import (
"fmt"
"strings"
"sync"
"time"
"github.com/lightninglabs/lightning-terminal/firewalldb"
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
@ -193,11 +192,9 @@ func (r *RequestLogger) addNewAction(ctx context.Context, ri *RequestInfo,
}
}
action := &firewalldb.Action{
SessionID: sessionID,
RPCMethod: ri.URI,
AttemptedAt: time.Now(),
State: firewalldb.ActionStateInit,
actionReq := &firewalldb.AddActionReq{
SessionID: sessionID,
RPCMethod: ri.URI,
}
if withPayloadData {
@ -211,19 +208,19 @@ func (r *RequestLogger) addNewAction(ctx context.Context, ri *RequestInfo,
return fmt.Errorf("unable to decode response: %v", err)
}
action.RPCParamsJson = jsonBytes
actionReq.RPCParamsJson = jsonBytes
meta := ri.MetaInfo
if meta != nil {
action.ActorName = meta.ActorName
action.FeatureName = meta.Feature
action.Trigger = meta.Trigger
action.Intent = meta.Intent
action.StructuredJsonData = meta.StructuredJsonData
actionReq.ActorName = meta.ActorName
actionReq.FeatureName = meta.Feature
actionReq.Trigger = meta.Trigger
actionReq.Intent = meta.Intent
actionReq.StructuredJsonData = meta.StructuredJsonData
}
}
locator, err := r.actionsDB.AddAction(ctx, action)
locator, err := r.actionsDB.AddAction(ctx, actionReq)
if err != nil {
return err
}

View file

@ -28,8 +28,10 @@ const (
ActionStateError ActionState = 3
)
// Action represents an RPC call made through the firewall.
type Action struct {
// AddActionReq is the request that is used to add a new Action to the database.
// It contains all the information that is needed to create a new Action in the
// ActionStateInit State.
type AddActionReq struct {
// SessionID is the ID of the session that this action belongs to.
// Note that this is not serialized on persistence since the action is
// already stored under a bucket identified by the session ID.
@ -59,6 +61,11 @@ type Action struct {
// RPCParams is the method parameters of the request in JSON form.
RPCParamsJson []byte
}
// Action represents an RPC call made through the firewall.
type Action struct {
AddActionReq
// AttemptedAt is the time at which this action was created.
AttemptedAt time.Time
@ -181,7 +188,7 @@ func WithActionState(state ActionState) ListActionOption {
// ActionsWriteDB is an abstraction over the Actions DB that will allow a
// caller to add new actions as well as change the values of an existing action.
type ActionsWriteDB interface {
AddAction(ctx context.Context, action *Action) (ActionLocator, error)
AddAction(ctx context.Context, req *AddActionReq) (ActionLocator, error)
SetActionState(ctx context.Context, al ActionLocator,
state ActionState, errReason string) error
}

View file

@ -53,8 +53,14 @@ var (
)
// AddAction serialises and adds an Action to the DB under the given sessionID.
func (db *BoltDB) AddAction(_ context.Context, action *Action) (ActionLocator,
error) {
func (db *BoltDB) AddAction(_ context.Context,
req *AddActionReq) (ActionLocator, error) {
action := &Action{
AddActionReq: *req,
AttemptedAt: db.clock.Now().UTC(),
State: ActionStateInit,
}
var buf bytes.Buffer
if err := SerializeAction(&buf, action); err != nil {

View file

@ -11,10 +11,13 @@ import (
)
var (
testTime1 = time.Unix(32100, 0)
testTime2 = time.Unix(12300, 0)
sessionID1 = intToSessionID(1)
sessionID2 = intToSessionID(2)
action1 = &Action{
action1Req = &AddActionReq{
SessionID: sessionID1,
ActorName: "Autopilot",
FeatureName: "auto-fees",
@ -23,11 +26,15 @@ var (
StructuredJsonData: "{\"something\":\"nothing\"}",
RPCMethod: "UpdateChanPolicy",
RPCParamsJson: []byte("new fee"),
AttemptedAt: time.Unix(32100, 0),
State: ActionStateDone,
}
action2 = &Action{
action1 = &Action{
AddActionReq: *action1Req,
AttemptedAt: testTime1,
State: ActionStateDone,
}
action2Req = &AddActionReq{
SessionID: sessionID2,
ActorName: "Autopilot",
FeatureName: "rebalancer",
@ -35,17 +42,21 @@ var (
Intent: "balance",
RPCMethod: "SendToRoute",
RPCParamsJson: []byte("hops, amount"),
AttemptedAt: time.Unix(12300, 0),
State: ActionStateInit,
}
action2 = &Action{
AddActionReq: *action2Req,
AttemptedAt: testTime2,
State: ActionStateInit,
}
)
// TestActionStorage tests that the ActionsListDB CRUD logic.
func TestActionStorage(t *testing.T) {
tmpDir := t.TempDir()
ctx := context.Background()
clock := clock.NewTestClock(testTime1)
db, err := NewBoltDB(tmpDir, "test.db", nil, clock.NewDefaultClock())
db, err := NewBoltDB(t.TempDir(), "test.db", nil, clock)
require.NoError(t, err)
t.Cleanup(func() {
_ = db.Close()
@ -67,10 +78,14 @@ func TestActionStorage(t *testing.T) {
require.NoError(t, err)
require.Len(t, actions, 0)
_, err = db.AddAction(ctx, action1)
locator1, err := db.AddAction(ctx, action1Req)
require.NoError(t, err)
err = db.SetActionState(ctx, locator1, ActionStateDone, "")
require.NoError(t, err)
locator2, err := db.AddAction(ctx, action2)
clock.SetTime(testTime2)
locator2, err := db.AddAction(ctx, action2Req)
require.NoError(t, err)
actions, _, _, err = db.ListActions(
@ -103,7 +118,7 @@ func TestActionStorage(t *testing.T) {
action2.State = ActionStateDone
assertEqualActions(t, action2, actions[0])
_, err = db.AddAction(ctx, action1)
_, err = db.AddAction(ctx, action1Req)
require.NoError(t, err)
// Check that providing no session id and no filter function returns
@ -154,7 +169,8 @@ func TestListActions(t *testing.T) {
actionIds := 0
addAction := func(sessionID [4]byte) {
actionIds++
action := &Action{
actionReq := &AddActionReq{
SessionID: sessionID,
ActorName: "Autopilot",
FeatureName: fmt.Sprintf("%d", actionIds),
@ -163,11 +179,9 @@ func TestListActions(t *testing.T) {
StructuredJsonData: "{\"something\":\"nothing\"}",
RPCMethod: "UpdateChanPolicy",
RPCParamsJson: []byte("new fee"),
AttemptedAt: time.Unix(32100, 0),
State: ActionStateDone,
}
_, err := db.AddAction(ctx, action)
_, err := db.AddAction(ctx, actionReq)
require.NoError(t, err)
}
@ -358,7 +372,7 @@ func TestListGroupActions(t *testing.T) {
require.Empty(t, al)
// Add an action under session 1.
_, err = db.AddAction(ctx, action1)
_, err = db.AddAction(ctx, action1Req)
require.NoError(t, err)
// There should now be one action in the group.
@ -368,7 +382,7 @@ func TestListGroupActions(t *testing.T) {
require.Equal(t, sessionID1, al[0].SessionID)
// Add an action under session 2.
_, err = db.AddAction(ctx, action2)
_, err = db.AddAction(ctx, action2Req)
require.NoError(t, err)
// There should now be actions in the group.

View file

@ -104,8 +104,8 @@ type PrivacyMapper interface {
// ActionDB is an interface that abstracts the database operations needed for
// the Action persistence and querying.
type ActionDB interface {
// AddAction persists the given action to the database.
AddAction(ctx context.Context, action *Action) (ActionLocator, error)
// AddAction persists a new action to the database.
AddAction(ctx context.Context, req *AddActionReq) (ActionLocator, error)
// SetActionState finds the action specified by the ActionLocator and
// sets its state to the given state.