storage

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 21, 2026 License: MIT Imports: 6 Imported by: 0

README

pkg/storage

This package handles request and environment persistence for Falcon. It provides YAML-based file I/O and a {{VAR}} variable substitution engine used across all saved requests and environments.

Package Overview

pkg/storage/
├── schema.go  # Data structures: Request, Environment, Collection
├── yaml.go    # YAML file read/write operations
└── env.go     # Variable substitution engine ({{VAR}} placeholders)

Data Structures

Request

Represents a saved API request:

type Request struct {
    Name        string            `yaml:"name"`
    Method      string            `yaml:"method"`
    URL         string            `yaml:"url"`
    Headers     map[string]string `yaml:"headers,omitempty"`
    Body        string            `yaml:"body,omitempty"`
    Description string            `yaml:"description,omitempty"`
}

Example YAML (.falcon/requests/get-users.yaml):

name: Get Users
method: GET
url: "{{BASE_URL}}/api/users"
headers:
  Authorization: "Bearer {{API_TOKEN}}"
  Content-Type: application/json
description: Fetches all users from the API
Environment

Represents a set of variables for a specific environment:

type Environment struct {
    Name      string            `yaml:"name"`
    Variables map[string]string `yaml:"variables"`
}

Example YAML (.falcon/environments/dev.yaml):

# Development environment
BASE_URL: http://localhost:3000
API_KEY: your-dev-api-key
Collection

A named group of related requests (reserved for future use):

type Collection struct {
    Name        string    `yaml:"name"`
    Description string    `yaml:"description"`
    Requests    []Request `yaml:"requests"`
}

YAML Operations (yaml.go)

Requests
// Save a request
err := storage.SaveRequest(".falcon/requests/get-users.yaml", request)

// Load a request
request, err := storage.LoadRequest(".falcon/requests/get-users.yaml")

// List all saved requests (returns names without extension)
names, err := storage.ListRequests(".falcon/requests/")
// Returns: []string{"get-users", "create-user", "health-check"}
Environments
// Save an environment
err := storage.SaveEnvironment(".falcon/environments/prod.yaml", env)

// Load an environment
env, err := storage.LoadEnvironment(".falcon/environments/prod.yaml")

// List all environments
names, err := storage.ListEnvironments(".falcon/environments/")
// Returns: []string{"dev", "staging", "prod"}

Variable Substitution (env.go)

Basic Substitution

Replace {{VAR}} placeholders with values from an environment:

variables := map[string]string{
    "BASE_URL":  "http://localhost:3000",
    "API_TOKEN": "abc123",
}

result := storage.SubstituteVariables("{{BASE_URL}}/api/users", variables)
// Result: "http://localhost:3000/api/users"
Full Request Substitution

Apply substitution to all fields of a request at once:

substituted := storage.SubstituteRequest(request, variables)
// substituted.URL     → "http://localhost:3000/api/users"
// substituted.Headers → {"Authorization": "Bearer abc123"}
Nested Variables

Variables can reference other variables and are resolved recursively:

# Environment
BASE_URL: "http://localhost:3000"
API_URL:  "{{BASE_URL}}/api"
USERS_URL: "{{API_URL}}/users"
result := storage.SubstituteVariables("{{USERS_URL}}", variables)
// Result: "http://localhost:3000/api/users"
Undefined Variables

Undefined placeholders are left unchanged:

result := storage.SubstituteVariables("{{UNDEFINED}}/path", variables)
// Result: "{{UNDEFINED}}/path"

File Layout

Falcon uses two persistent folders:

~/.falcon/                   # Global — shared across all projects
├── config.yaml              # LLM provider credentials
└── memory.json              # Persistent agent memory (JSON)

.falcon/                     # Per-project runtime data
├── config.yaml              # Project config (framework, tool limits, web_ui)
├── manifest.json            # Parsed endpoint graph (JSON)
├── falcon.md                # API knowledge base
├── spec.yaml                # Ingested API spec (YAML)
├── variables.json           # Global variables
├── requests/                # Saved API requests
│   ├── get-users.yaml
│   └── create-user.yaml
├── environments/            # Environment variable files
│   ├── dev.yaml
│   ├── staging.yaml
│   └── prod.yaml
├── baselines/               # Regression test snapshots
├── sessions/                # Session audit logs
├── flows/                   # Multi-step API flows
└── reports/                 # Test reports (Markdown)

Usage Patterns

Save → Switch Env → Load → Execute
// 1. Save a request template
storage.SaveRequest(".falcon/requests/login.yaml", &storage.Request{
    Name:   "Login",
    Method: "POST",
    URL:    "{{BASE_URL}}/auth/login",
    Headers: map[string]string{"Content-Type": "application/json"},
    Body:   `{"username":"{{TEST_USER}}","password":"{{TEST_PASS}}"}`,
})

// 2. Load environment
env, _ := storage.LoadEnvironment(".falcon/environments/dev.yaml")

// 3. Load and substitute request
req, _ := storage.LoadRequest(".falcon/requests/login.yaml")
ready := storage.SubstituteRequest(req, env.Variables)

// 4. ready.URL, ready.Headers, ready.Body are all substituted
Multi-Environment Request Templates

Define the request once, use different credentials per environment:

# .falcon/environments/dev.yaml
BASE_URL: http://localhost:3000
TEST_USER: devuser
TEST_PASS: devpass

# .falcon/environments/staging.yaml
BASE_URL: https://staging.example.com
TEST_USER: stageuser
TEST_PASS: stagepass

Error Handling

All functions return descriptive errors:

req, err := storage.LoadRequest(".falcon/requests/missing.yaml")
// err: "request file not found: .falcon/requests/missing.yaml"

env, err := storage.LoadEnvironment(".falcon/environments/bad.yaml")
// err: "invalid YAML in .falcon/environments/bad.yaml: ..."

Testing

go test ./pkg/storage/...

Example:

func TestSubstituteVariables(t *testing.T) {
    vars := map[string]string{
        "BASE_URL": "http://localhost",
        "PORT":     "8080",
    }

    tests := []struct{ input, want string }{
        {"{{BASE_URL}}", "http://localhost"},
        {"{{BASE_URL}}:{{PORT}}", "http://localhost:8080"},
        {"{{UNDEFINED}}", "{{UNDEFINED}}"},
        {"no vars", "no vars"},
    }

    for _, tt := range tests {
        got := storage.SubstituteVariables(tt.input, vars)
        if got != tt.want {
            t.Errorf("got %q, want %q", got, tt.want)
        }
    }
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GetEnvironmentsDir

func GetEnvironmentsDir(baseDir string) string

GetEnvironmentsDir returns the environments directory path

func GetFlowsDir

func GetFlowsDir(baseDir string) string

GetFlowsDir returns the flows directory path

func GetRequestsDir

func GetRequestsDir(baseDir string) string

GetRequestsDir returns the requests directory path

func ListEnvironments

func ListEnvironments(baseDir string) ([]string, error)

ListEnvironments lists all environment files

func ListRequests

func ListRequests(baseDir string) ([]string, error)

ListRequests lists all saved requests in the requests directory

func LoadEnvironment

func LoadEnvironment(filePath string) (map[string]string, error)

LoadEnvironment loads environment variables from a YAML file

func SaveEnvironment

func SaveEnvironment(env map[string]string, filePath string) error

SaveEnvironment saves environment variables to a YAML file

func SaveRequest

func SaveRequest(req Request, filePath string) error

SaveRequest saves a request to a YAML file

func SubstituteVariables

func SubstituteVariables(text string, env map[string]string) string

SubstituteVariables replaces {{VAR}} placeholders with values from the environment

Types

type Collection

type Collection struct {
	Name        string    `yaml:"name"`                  // Collection name
	Description string    `yaml:"description,omitempty"` // Optional description
	Requests    []Request `yaml:"requests,omitempty"`    // List of requests in the collection
}

Collection represents a folder of related requests.

type Environment

type Environment struct {
	Name      string            `yaml:"name"`    // Environment name (e.g., "dev", "prod")
	Variables map[string]string `yaml:",inline"` // Key-value pairs for variables
}

Environment represents a set of environment variables.

type Request

type Request struct {
	Name    string            `yaml:"name"`              // Unique name for the request
	Method  string            `yaml:"method"`            // HTTP method (GET, POST, etc.)
	URL     string            `yaml:"url"`               // Request URL (can contain variables)
	Headers map[string]string `yaml:"headers,omitempty"` // HTTP headers
	Query   map[string]string `yaml:"query,omitempty"`   // Query parameters
	Body    interface{}       `yaml:"body,omitempty"`    // Request body (JSON or string)
}

Request represents a saved API request in YAML format.

func ApplyEnvironment

func ApplyEnvironment(req *Request, env map[string]string) *Request

ApplyEnvironment applies environment variables to a request

func LoadRequest

func LoadRequest(filePath string) (*Request, error)

LoadRequest loads a request from a YAML file

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL