Skip to content
Draft
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
29 changes: 0 additions & 29 deletions backend/internal/activities/deployer_activities.go
Original file line number Diff line number Diff line change
Expand Up @@ -711,35 +711,6 @@ func registerDeploymentActivities(engine *ewf.Engine, metrics *metrics.Metrics,
engine.RegisterTemplate(constants.WorkflowRollbackFailedAddNode, &rollbackAddNodeWFTemplate)
}

func getFromState[T any](state ewf.State, key string) (T, error) {
value, ok := state[key]
if !ok {
var zero T
return zero, fmt.Errorf("missing '%s' in state", key)
}

// Try direct type assertion first (for newly created values)
if val, ok := value.(T); ok {
return val, nil
}

// Handle the case where value was serialized/deserialized and became a map
// Use JSON marshaling/unmarshaling to convert map to struct
valueBytes, err := json.Marshal(value)
if err != nil {
var zero T
return zero, fmt.Errorf("failed to marshal %s value: %w", key, err)
}

var result T
if err := json.Unmarshal(valueBytes, &result); err != nil {
var zero T
return zero, fmt.Errorf("failed to unmarshal %s: %w", key, err)
}

return result, nil
}

func getConfig(state ewf.State) (statemanager.ClientConfig, error) {
value, ok := state["config"]
if !ok {
Expand Down
67 changes: 30 additions & 37 deletions backend/internal/activities/node_activities.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@ import (

func CreateIdentityStep() ewf.StepFn {
return func(ctx context.Context, state ewf.State) error {

mnemonicVal, ok := state["mnemonic"]
if !ok {
return fmt.Errorf("missing 'mnemonic' in state")
}
mnemonic, ok := mnemonicVal.(string)
if !ok {
return fmt.Errorf("'mnemonic' in state is not a string")
mnemonic, err := getFromState[string](state, "mnemonic")
if err != nil {
return err
}

identity, err := substrate.NewIdentityFromSr25519Phrase(mnemonic)
if err != nil {
return fmt.Errorf("failed to create identity: %w", err)
Expand All @@ -34,17 +30,19 @@ func CreateIdentityStep() ewf.StepFn {

func ReserveNodeStep(db models.DB, substrateClient *substrate.Substrate) ewf.StepFn {
return func(ctx context.Context, state ewf.State) error {
userID, ok := state["user_id"].(int)
if !ok {
return fmt.Errorf("missing or invalid 'user_id' in state")
userID, err := getFromState[int](state, "user_id")
if err != nil {
return err
}
nodeID, ok := state["node_id"].(uint32)
if !ok {
return fmt.Errorf("missing or invalid 'node_id' in state")

nodeID, err := getFromState[uint32](state, "node_id")
if err != nil {
return err
}
identity, ok := state["identity"].(substrate.Identity)
if !ok {
return fmt.Errorf("missing or invalid 'identity' in state")

identity, err := getFromState[substrate.Identity](state, "identity")
if err != nil {
return err
}

// Reserve the node
Expand All @@ -70,13 +68,14 @@ func ReserveNodeStep(db models.DB, substrateClient *substrate.Substrate) ewf.Ste

func UnreserveNodeStep(db models.DB, substrateClient *substrate.Substrate) ewf.StepFn {
return func(ctx context.Context, state ewf.State) error {
contractID, ok := state["contract_id"].(uint64)
if !ok {
return fmt.Errorf("missing or invalid 'contract_id' in state")
contractID, err := getFromState[uint64](state, "contract_id")
if err != nil {
return err
}
mnemonic, ok := state["mnemonic"].(string)
if !ok {
return fmt.Errorf("missing or invalid 'mnemonic' in state")

mnemonic, err := getFromState[string](state, "mnemonic")
if err != nil {
return err
}

identity, err := substrate.NewIdentityFromSr25519Phrase(mnemonic)
Expand All @@ -101,31 +100,25 @@ func UnreserveNodeStep(db models.DB, substrateClient *substrate.Substrate) ewf.S
// VerifyNodeStateStep checks if node has reached the desired state
func VerifyNodeStateStep(proxyClient proxy.Client) ewf.StepFn {
return func(ctx context.Context, state ewf.State) error {

targetStatus, ok := state["target_status"].(string)
if !ok {
return fmt.Errorf("missing or invalid 'target_status' in state")
}

nodeID, exists := state["node_id"]
if !exists {
return fmt.Errorf("missing or invalid 'node_id' in state")
targetStatus, err := getFromState[string](state, "target_status")
if err != nil {
return err
}

nodeIDUint32, ok := nodeID.(uint32)
if !ok {
return fmt.Errorf("node_id in state is not a uint32")
nodeID, err := getFromState[uint32](state, "node_id")
if err != nil {
return err
}

node, err := proxyClient.Node(ctx, nodeIDUint32)
node, err := proxyClient.Node(ctx, nodeID)
if err != nil {
return fmt.Errorf("failed to get node: %w", err)
}

reached := targetStatus == constants.NodeRentable && node.Rentable || targetStatus == constants.NodeRented && !node.Rentable

if !reached {
return fmt.Errorf("node %d has not reached target status '%s' (current: rentable=%v)", nodeIDUint32, targetStatus, node.Rentable)
return fmt.Errorf("node %d has not reached target status '%s' (current: rentable=%v)", nodeID, targetStatus, node.Rentable)
}

return nil
Expand Down
4 changes: 4 additions & 0 deletions backend/internal/activities/notification_activities.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,22 @@ func SendNotification(db models.DB, notifier notification.Notifier) ewf.StepFn {
if !ok {
return fmt.Errorf("missing notification in workflow state")
}

notif, ok := raw.(*models.Notification)
if !ok || notif == nil {
return fmt.Errorf("invalid notification in workflow state")
}

if !slices.Contains(notif.Channels, notifier.GetType()) {
logger.GetLogger().Debug().Msgf("SendNotification: step skipped for channel %s (not in notification channels)", notifier.GetType())
return nil
}

user, err := db.GetUserByID(notif.UserID)
if err != nil {
return fmt.Errorf("failed to get user by ID (id: %v): %w", notif.UserID, err)
}

if err := notifier.Notify(*notif, user.Email); err != nil {
return fmt.Errorf("failed to send notification (id: %v) to %s: %w", notif.ID, notifier.GetType(), err)
}
Expand Down
39 changes: 39 additions & 0 deletions backend/internal/activities/state_helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package activities

import (
"encoding/json"
"fmt"

"github.com/xmonader/ewf"
)

// getFromState is a generic helper function to extract and type-cast values from workflow state.
// It handles both direct type assertions and JSON-based conversions for serialized/deserialized values.
func getFromState[T any](state ewf.State, key string) (T, error) {
value, ok := state[key]
if !ok {
var zero T
return zero, fmt.Errorf("missing '%s' in state", key)
}

// Try direct type assertion first (for newly created values)
if val, ok := value.(T); ok {
return val, nil
}

// Handle the case where value was serialized/deserialized and became a map
// Use JSON marshaling/unmarshaling to convert map to struct
valueBytes, err := json.Marshal(value)
if err != nil {
var zero T
return zero, fmt.Errorf("failed to marshal %s value: %w", key, err)
}

var result T
if err := json.Unmarshal(valueBytes, &result); err != nil {
var zero T
return zero, fmt.Errorf("failed to unmarshal %s: %w", key, err)
}

return result, nil
}
Loading