How to Run Integrations Locally

TL;DR

Test and validate an OAA integration locally before deploying to production.

Understanding the run command

Every tutorial and example in this series runs integrations with this pattern:

uv tool run --from oaa-runner oaa app run --config-file=config.yaml

Breaking it down:

  • uv tool run — runs a command provided by an installed tool, without activating a virtual environment

  • --from oaa-runner — specifies the PyPI package that provides the command (oaa-runner is the package name)

  • oaa — the CLI entry point installed by that package (the command you’re actually running)

  • app run — the subcommand: load and process a Custom App integration

The package is named oaa-runner on PyPI; the CLI it installs is named oaa. The --from flag is what connects them.

!!! note “Preferred Command Alias” Use oaa app as the primary command. The older oaa custom_app alias is deprecated and will be removed in a future release. Both commands are currently fully equivalent.

Dry runs with --no-push

Adding --no-push loads and validates data locally without sending anything to Veza:

uv tool run --from oaa-runner oaa app run --config-file=config.yaml --no-push

Environment variables are not required during a dry run. Any !env config values are evaluated but the actual push step — the one that would fail without valid credentials — is skipped. You can safely run --no-push without setting VEZA_URL, VEZA_API_KEY, or any csv_provider_id env vars.

Prerequisites

  • OAA integration project set up with config.yaml

  • Python 3.9 or later installed

  • uv tool installed (see How do I install OAA?)

  • All credentials configured in .env file

Steps

Step 1: Validate Configuration

Before running, validate that your config.yaml is correct:

cd /path/to/your/integration

uv tool run --from oaa-runner oaa app validate --config=config.yaml

This checks:

  • ✅ YAML syntax is valid

  • ✅ All required fields are present

  • ✅ Module imports work

  • ✅ Connections are configured correctly

  • ✅ Sources are defined properly

Step 2: Set Up Environment

Ensure your .env file has all required credentials:

# .env file
API_KEY=your_api_key_here
ANOTHER_KEY=value_here

# Never commit .env to git!
echo ".env" >> .gitignore

Step 3: Run Integration Locally

Execute the integration with JSON output:

uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json

This:

  • ✅ Fetches data from sources

  • ✅ Applies transformations

  • ✅ Maps fields to OAA

  • ✅ Generates output.json

  • ✅ Prints results to console

Step 4: Examine Output

Check the generated output.json:

# View overall structure
jq '.' output.json | head -50

# Count entities by type
jq '.local_users | length' output.json
jq '.local_roles | length' output.json
jq '.local_groups | length' output.json

# View first user
jq '.local_users[0]' output.json

# View relationships
jq '.relationships | length' output.json
jq '.relationships[] | select(.src_type=="LocalUser")' output.json

Step 5: Enable Debug Logging

For more detailed output:

uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json \
  --debug

This shows:

  • ✅ Detailed transformation steps

  • ✅ Field mapping operations

  • ✅ Hook executions

  • ✅ Relationship building

Common Commands

Validate Only (Dry Run)

Check configuration without fetching data:

uv tool run --from oaa-runner oaa app validate --config=config.yaml

Show Configuration

Display the loaded configuration:

uv tool run --from oaa-runner oaa app show_config --config=config.yaml

Run with Priority Push

Test with priority_push flag (for Veza conflicts):

uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json \
  --priority-push

Run Specific Source Only

Test individual source (if supported):

uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json \
  --source=users  # Only fetch 'users' source

Troubleshooting

Error: Config file not found

# Make sure you're in the right directory
cd /path/to/integration

# And config.yaml exists
ls config.yaml

Error: Module import failed

# Check modules are in logic_modules list
cat config.yaml | grep logic_modules

# Verify files exist
ls modules/

Error: Connection failed

# Verify credentials in .env
cat .env

# Test connection manually
python3 -c "import os; print(os.getenv('API_KEY'))"

Error: YAML syntax error

Validate YAML syntax:

# Using Python
python3 -m yaml config.yaml

# Or using online validator
# Copy config.yaml to https://www.yamllint.com/

No data in output.json

Check logs for:

# Look for ERROR or WARNING in output
uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json 2>&1 | grep -i error

Common causes:

  • API credentials invalid

  • Source endpoint incorrect

  • No data in source system

  • Field mapping errors

Inspecting Output

Count All Entities

jq '
  {
    users: (.local_users | length),
    roles: (.local_roles | length),
    groups: (.local_groups | length),
    resources: (.resources | length),
    relationships: (.relationships | length)
  }
' output.json

Find Missing Email Addresses

jq '.local_users[] | select(.email == null or .email == "")' output.json

List All Custom Properties

jq '.local_users[0].custom_properties' output.json

View Relationship Graph

jq '.relationships[]' output.json | jq -s 'group_by(.src_type)'

Find Relationships for Specific User

jq '.relationships[] | select(.src_id=="user123")' output.json

Performance Monitoring

Track Integration Time

time uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json

Monitor Memory Usage

# On macOS
/usr/bin/time -v uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json

# On Linux
command time -v uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json

Local Development Workflow

  1. Initial Setup

    cd /path/to/integration
    cp sample.env .env
    # Edit .env with your credentials
    
  2. Validate Configuration

    uv tool run --from oaa-runner oaa app validate --config=config.yaml
    
  3. Run with Debug Output

    uv tool run --from oaa-runner oaa app run \
      --config=config.yaml \
      --save-json \
      --debug
    
  4. Inspect Results

    jq '.' output.json | less
    
  5. Fix Issues

    • Edit config.yaml or modules/hooks.py

    • Re-run from step 2

  6. Commit When Ready

    git add config.yaml modules/
    git commit -m "Add integration for XYZ"
    

Examples

Test ThousandEyes Integration

cd /path/to/netapp/thousandeyes

# Validate
uv tool run --from oaa-runner oaa app validate --config=config.yaml

# Run with debug
uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json \
  --debug

# Check output
jq '.local_users | length' output.json
jq '.local_roles | length' output.json
jq '.relationships[] | select(.src_type=="LocalUser")' output.json

Test Abnormal Integration

cd /path/to/gxo/abnormal

uv tool run --from oaa-runner oaa app run \
  --config=config.yaml \
  --save-json

# View first user with custom properties
jq '.local_users[0] | {name, email, custom_properties}' output.json

Next Steps