258 lines
6.4 KiB
Go
258 lines
6.4 KiB
Go
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,
|
|
}
|
|
}
|