Feat/Add Crypto Libs #3
257
internal/keybase/actions_invocation.go
Normal file
257
internal/keybase/actions_invocation.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package keybase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// =============================================================================
|
||||
// INVOCATION ACTIONS (UCAN v1.0.0-rc.1)
|
||||
// =============================================================================
|
||||
|
||||
// InvocationResult represents an invocation in API responses.
|
||||
type InvocationResult struct {
|
||||
ID int64 `json:"id"`
|
||||
CID string `json:"cid"`
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Audience string `json:"aud,omitempty"`
|
||||
Command string `json:"cmd"`
|
||||
Proofs string `json:"prf"`
|
||||
Expiration string `json:"exp,omitempty"`
|
||||
IssuedAt string `json:"iat,omitempty"`
|
||||
ExecutedAt string `json:"executed_at,omitempty"`
|
||||
ResultCID string `json:"result_cid,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// StoreInvocationParams contains parameters for storing an invocation.
|
||||
type StoreInvocationParams struct {
|
||||
CID string `json:"cid"`
|
||||
Envelope []byte `json:"envelope"`
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Audience string `json:"aud,omitempty"`
|
||||
Command string `json:"cmd"`
|
||||
Proofs string `json:"prf"`
|
||||
Expiration string `json:"exp,omitempty"`
|
||||
IssuedAt string `json:"iat,omitempty"`
|
||||
}
|
||||
|
||||
// StoreInvocation stores a new UCAN invocation envelope.
|
||||
func (am *ActionManager) StoreInvocation(ctx context.Context, params StoreInvocationParams) (*InvocationResult, error) {
|
||||
am.kb.mu.Lock()
|
||||
defer am.kb.mu.Unlock()
|
||||
|
||||
if am.kb.didID == 0 {
|
||||
return nil, fmt.Errorf("DID not initialized")
|
||||
}
|
||||
|
||||
var aud, exp, iat *string
|
||||
if params.Audience != "" {
|
||||
aud = ¶ms.Audience
|
||||
}
|
||||
if params.Expiration != "" {
|
||||
exp = ¶ms.Expiration
|
||||
}
|
||||
if params.IssuedAt != "" {
|
||||
iat = ¶ms.IssuedAt
|
||||
}
|
||||
|
||||
inv, err := am.kb.queries.CreateInvocation(ctx, CreateInvocationParams{
|
||||
DidID: am.kb.didID,
|
||||
Cid: params.CID,
|
||||
Envelope: params.Envelope,
|
||||
Iss: params.Issuer,
|
||||
Sub: params.Subject,
|
||||
Aud: aud,
|
||||
Cmd: params.Command,
|
||||
Prf: params.Proofs,
|
||||
Exp: exp,
|
||||
Iat: iat,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create invocation: %w", err)
|
||||
}
|
||||
|
||||
return invocationToResult(inv), nil
|
||||
}
|
||||
|
||||
// GetInvocationByCID retrieves an invocation by its CID.
|
||||
func (am *ActionManager) GetInvocationByCID(ctx context.Context, cid string) (*InvocationResult, error) {
|
||||
am.kb.mu.RLock()
|
||||
defer am.kb.mu.RUnlock()
|
||||
|
||||
inv, err := am.kb.queries.GetInvocationByCID(ctx, cid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get invocation: %w", err)
|
||||
}
|
||||
|
||||
return invocationToResult(inv), nil
|
||||
}
|
||||
|
||||
// GetInvocationEnvelope retrieves the raw CBOR envelope for an invocation.
|
||||
func (am *ActionManager) GetInvocationEnvelope(ctx context.Context, cid string) ([]byte, error) {
|
||||
am.kb.mu.RLock()
|
||||
defer am.kb.mu.RUnlock()
|
||||
|
||||
envelope, err := am.kb.queries.GetInvocationEnvelopeByCID(ctx, cid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get invocation envelope: %w", err)
|
||||
}
|
||||
|
||||
return envelope, nil
|
||||
}
|
||||
|
||||
// ListInvocations returns recent invocations for the current DID.
|
||||
func (am *ActionManager) ListInvocations(ctx context.Context, limit int64) ([]InvocationResult, error) {
|
||||
am.kb.mu.RLock()
|
||||
defer am.kb.mu.RUnlock()
|
||||
|
||||
if am.kb.didID == 0 {
|
||||
return []InvocationResult{}, nil
|
||||
}
|
||||
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
invocations, err := am.kb.queries.ListInvocationsByDID(ctx, ListInvocationsByDIDParams{
|
||||
DidID: am.kb.didID,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list invocations: %w", err)
|
||||
}
|
||||
|
||||
results := make([]InvocationResult, len(invocations))
|
||||
for i, inv := range invocations {
|
||||
results[i] = *invocationToResult(inv)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// ListInvocationsByCommand returns invocations for a specific command.
|
||||
func (am *ActionManager) ListInvocationsByCommand(ctx context.Context, cmd string, limit int64) ([]InvocationResult, error) {
|
||||
am.kb.mu.RLock()
|
||||
defer am.kb.mu.RUnlock()
|
||||
|
||||
if am.kb.didID == 0 {
|
||||
return []InvocationResult{}, nil
|
||||
}
|
||||
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
invocations, err := am.kb.queries.ListInvocationsForCommand(ctx, ListInvocationsForCommandParams{
|
||||
DidID: am.kb.didID,
|
||||
Cmd: cmd,
|
||||
Limit: limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list invocations for command: %w", err)
|
||||
}
|
||||
|
||||
results := make([]InvocationResult, len(invocations))
|
||||
for i, inv := range invocations {
|
||||
results[i] = *invocationToResult(inv)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// ListPendingInvocations returns invocations that haven't been executed yet.
|
||||
func (am *ActionManager) ListPendingInvocations(ctx context.Context) ([]InvocationResult, error) {
|
||||
am.kb.mu.RLock()
|
||||
defer am.kb.mu.RUnlock()
|
||||
|
||||
if am.kb.didID == 0 {
|
||||
return []InvocationResult{}, nil
|
||||
}
|
||||
|
||||
invocations, err := am.kb.queries.ListPendingInvocations(ctx, am.kb.didID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list pending invocations: %w", err)
|
||||
}
|
||||
|
||||
results := make([]InvocationResult, len(invocations))
|
||||
for i, inv := range invocations {
|
||||
results[i] = *invocationToResult(inv)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// MarkInvocationExecuted marks an invocation as executed with an optional result CID.
|
||||
func (am *ActionManager) MarkInvocationExecuted(ctx context.Context, cid string, resultCID string) error {
|
||||
am.kb.mu.Lock()
|
||||
defer am.kb.mu.Unlock()
|
||||
|
||||
var result *string
|
||||
if resultCID != "" {
|
||||
result = &resultCID
|
||||
}
|
||||
|
||||
err := am.kb.queries.MarkInvocationExecuted(ctx, MarkInvocationExecutedParams{
|
||||
ResultCid: result,
|
||||
Cid: cid,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("mark invocation executed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanOldInvocations removes invocations older than 90 days.
|
||||
func (am *ActionManager) CleanOldInvocations(ctx context.Context) error {
|
||||
am.kb.mu.Lock()
|
||||
defer am.kb.mu.Unlock()
|
||||
|
||||
if err := am.kb.queries.CleanOldInvocations(ctx); err != nil {
|
||||
return fmt.Errorf("clean old invocations: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// invocationToResult converts a UcanInvocation to InvocationResult.
|
||||
func invocationToResult(inv UcanInvocation) *InvocationResult {
|
||||
audience := ""
|
||||
if inv.Aud != nil {
|
||||
audience = *inv.Aud
|
||||
}
|
||||
expiration := ""
|
||||
if inv.Exp != nil {
|
||||
expiration = *inv.Exp
|
||||
}
|
||||
issuedAt := ""
|
||||
if inv.Iat != nil {
|
||||
issuedAt = *inv.Iat
|
||||
}
|
||||
executedAt := ""
|
||||
if inv.ExecutedAt != nil {
|
||||
executedAt = *inv.ExecutedAt
|
||||
}
|
||||
resultCID := ""
|
||||
if inv.ResultCid != nil {
|
||||
resultCID = *inv.ResultCid
|
||||
}
|
||||
|
||||
return &InvocationResult{
|
||||
ID: inv.ID,
|
||||
CID: inv.Cid,
|
||||
Issuer: inv.Iss,
|
||||
Subject: inv.Sub,
|
||||
Audience: audience,
|
||||
Command: inv.Cmd,
|
||||
Proofs: inv.Prf,
|
||||
Expiration: expiration,
|
||||
IssuedAt: issuedAt,
|
||||
ExecutedAt: executedAt,
|
||||
ResultCID: resultCID,
|
||||
CreatedAt: inv.CreatedAt,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user