Files
attune/docs/workflows/rule-parameter-mapping.md
2026-02-04 17:46:30 -06:00

17 KiB

Rule Parameter Mapping

Overview

Rules in Attune can specify parameters to pass to actions when triggered. These parameters can be:

  1. Static values - Hard-coded values defined in the rule
  2. Dynamic from trigger payload - Values extracted from the event that triggered the rule
  3. Dynamic from pack config - Values from the pack's configuration

This enables flexible parameter passing without hardcoding values or requiring custom code.


Parameter Mapping Format

Rule action_params uses a JSON object where each value can be:

  • Static: A literal value (string, number, boolean, object, array)
  • Dynamic: A template string using {{ }} syntax to reference runtime values

Template Syntax

{{ source.path.to.value }}

Available Sources:

  • trigger.payload.* - Data from the event payload
  • pack.config.* - Configuration values from the pack
  • system.* - System-provided values (timestamp, execution context)

Static Parameter Example

The simplest form - just pass fixed values to the action:

{
  "ref": "slack.notify_on_error",
  "pack_ref": "slack",
  "trigger_ref": "core.error_event",
  "action_ref": "slack.post_message",
  "action_params": {
    "channel": "#alerts",
    "message": "An error occurred in the system",
    "color": "danger"
  }
}

When this rule triggers, the action receives exactly these parameters.


Dynamic Parameters from Trigger Payload

Extract values from the event that triggered the rule.

Example: Alert with Event Data

Trigger Payload:

{
  "severity": "error",
  "service": "api-gateway",
  "message": "Database connection failed",
  "timestamp": "2024-01-15T10:30:00Z",
  "metadata": {
    "host": "api-01.example.com",
    "error_code": "DB_CONN_TIMEOUT"
  }
}

Rule Definition:

{
  "ref": "alerts.error_notification",
  "pack_ref": "alerts",
  "trigger_ref": "core.error_event",
  "action_ref": "slack.post_message",
  "action_params": {
    "channel": "#incidents",
    "message": "Error in {{ trigger.payload.service }}: {{ trigger.payload.message }}",
    "severity": "{{ trigger.payload.severity }}",
    "host": "{{ trigger.payload.metadata.host }}",
    "timestamp": "{{ trigger.payload.timestamp }}"
  }
}

Resulting Action Parameters:

{
  "channel": "#incidents",
  "message": "Error in api-gateway: Database connection failed",
  "severity": "error",
  "host": "api-01.example.com",
  "timestamp": "2024-01-15T10:30:00Z"
}

Dynamic Parameters from Pack Config

Use configuration values stored at the pack level (useful for API keys, URLs, etc.).

Example: Using Pack Configuration

Pack Configuration:

{
  "ref": "slack",
  "config": {
    "api_token": "xoxb-1234567890-abcdefghijk",
    "default_channel": "#general",
    "webhook_url": "https://hooks.slack.com/services/...",
    "bot_name": "Attune Bot"
  }
}

Rule Definition:

{
  "ref": "slack.auto_notify",
  "pack_ref": "slack",
  "trigger_ref": "core.notification_event",
  "action_ref": "slack.post_message",
  "action_params": {
    "token": "{{ pack.config.api_token }}",
    "channel": "{{ pack.config.default_channel }}",
    "username": "{{ pack.config.bot_name }}",
    "message": "{{ trigger.payload.message }}"
  }
}

Benefits:

  • Secrets stored in pack config, not in rules
  • Easy to update credentials without changing rules
  • Reuse configuration across multiple rules

Mixed Parameters (Static + Dynamic)

Combine static and dynamic values in the same rule:

{
  "ref": "github.create_issue",
  "pack_ref": "github",
  "trigger_ref": "core.error_event",
  "action_ref": "github.create_issue",
  "action_params": {
    "repo": "myorg/myrepo",
    "token": "{{ pack.config.github_token }}",
    "title": "Error: {{ trigger.payload.message }}",
    "body": "Service {{ trigger.payload.service }} reported an error at {{ trigger.payload.timestamp }}",
    "labels": ["bug", "automated"],
    "assignees": ["oncall"]
  }
}

Nested Object Access

Access nested properties using dot notation:

{
  "action_params": {
    "user_id": "{{ trigger.payload.user.id }}",
    "user_name": "{{ trigger.payload.user.profile.name }}",
    "metadata": {
      "ip_address": "{{ trigger.payload.request.client_ip }}",
      "user_agent": "{{ trigger.payload.request.headers.user_agent }}"
    }
  }
}

Array Access

Access array elements by index:

{
  "action_params": {
    "first_error": "{{ trigger.payload.errors.0 }}",
    "primary_tag": "{{ trigger.payload.tags.0 }}"
  }
}

Default Values and Fallbacks

Provide default values when the referenced field doesn't exist:

{
  "action_params": {
    "priority": "{{ trigger.payload.priority | default: 'medium' }}",
    "assignee": "{{ trigger.payload.assignee | default: 'unassigned' }}"
  }
}

Type Preservation

Template values preserve their JSON types:

{
  "action_params": {
    "count": "{{ trigger.payload.count }}",          // Number: 42
    "enabled": "{{ trigger.payload.enabled }}",      // Boolean: true
    "tags": "{{ trigger.payload.tags }}",            // Array: ["a", "b"]
    "metadata": "{{ trigger.payload.metadata }}"     // Object: {"key": "value"}
  }
}

System Variables

Access system-provided values:

{
  "action_params": {
    "execution_time": "{{ system.timestamp }}",
    "rule_id": "{{ system.rule.id }}",
    "rule_ref": "{{ system.rule.ref }}",
    "event_id": "{{ system.event.id }}",
    "enforcement_id": "{{ system.enforcement.id }}"
  }
}

String Interpolation

Embed multiple values in a single string:

{
  "action_params": {
    "message": "User {{ trigger.payload.user_id }} performed {{ trigger.payload.action }} at {{ system.timestamp }}",
    "subject": "[{{ trigger.payload.severity | upper }}] {{ trigger.payload.service }} Alert"
  }
}

Filters (Future Enhancement)

Apply transformations to values:

{
  "action_params": {
    "uppercase_name": "{{ trigger.payload.name | upper }}",
    "lowercase_email": "{{ trigger.payload.email | lower }}",
    "formatted_date": "{{ trigger.payload.timestamp | date: '%Y-%m-%d' }}",
    "truncated": "{{ trigger.payload.message | truncate: 100 }}"
  }
}

Available Filters:

  • upper - Convert to uppercase
  • lower - Convert to lowercase
  • trim - Remove whitespace
  • default: <value> - Use default if null/missing
  • date: <format> - Format timestamp
  • truncate: <length> - Truncate string
  • json - Serialize to JSON string
  • base64 - Base64 encode
  • length - Get length/count

Real-World Examples

1. Webhook to Slack Alert

{
  "ref": "monitoring.webhook_to_slack",
  "pack_ref": "monitoring",
  "trigger_ref": "core.webhook",
  "action_ref": "slack.post_message",
  "action_params": {
    "channel": "{{ pack.config.alert_channel }}",
    "token": "{{ pack.config.slack_token }}",
    "message": "⚠️ Alert from {{ trigger.payload.source }}: {{ trigger.payload.message }}",
    "attachments": [
      {
        "color": "{{ trigger.payload.severity | default: 'warning' }}",
        "fields": [
          {
            "title": "Service",
            "value": "{{ trigger.payload.service }}",
            "short": true
          },
          {
            "title": "Environment",
            "value": "{{ trigger.payload.environment | default: 'production' }}",
            "short": true
          }
        ],
        "footer": "Attune Automation",
        "ts": "{{ system.timestamp }}"
      }
    ]
  }
}

2. Error to Ticket System

{
  "ref": "errors.create_ticket",
  "pack_ref": "errors",
  "trigger_ref": "core.error_event",
  "action_ref": "jira.create_issue",
  "action_params": {
    "project": "{{ pack.config.jira_project }}",
    "auth": {
      "username": "{{ pack.config.jira_username }}",
      "token": "{{ pack.config.jira_token }}"
    },
    "issuetype": "Bug",
    "summary": "[{{ trigger.payload.severity }}] {{ trigger.payload.service }}: {{ trigger.payload.message }}",
    "description": {
      "type": "doc",
      "content": [
        {
          "type": "paragraph",
          "content": [
            {
              "type": "text",
              "text": "Error Details:\n\nService: {{ trigger.payload.service }}\nHost: {{ trigger.payload.host }}\nTimestamp: {{ trigger.payload.timestamp }}\n\nStack Trace:\n{{ trigger.payload.stack_trace }}"
            }
          ]
        }
      ]
    },
    "priority": "{{ trigger.payload.priority | default: 'Medium' }}",
    "labels": ["automated", "{{ trigger.payload.service }}"]
  }
}

3. Metric Threshold to PagerDuty

{
  "ref": "monitoring.critical_alert",
  "pack_ref": "monitoring",
  "trigger_ref": "metrics.threshold_exceeded",
  "action_ref": "pagerduty.trigger_incident",
  "action_params": {
    "routing_key": "{{ pack.config.pagerduty_routing_key }}",
    "event_action": "trigger",
    "payload": {
      "summary": "{{ trigger.payload.metric_name }} exceeded threshold on {{ trigger.payload.host }}",
      "severity": "critical",
      "source": "{{ trigger.payload.host }}",
      "custom_details": {
        "metric": "{{ trigger.payload.metric_name }}",
        "current_value": "{{ trigger.payload.current_value }}",
        "threshold": "{{ trigger.payload.threshold }}",
        "duration": "{{ trigger.payload.duration_seconds }}s"
      }
    },
    "dedup_key": "{{ trigger.payload.host }}_{{ trigger.payload.metric_name }}"
  }
}

4. Timer to HTTP Request

{
  "ref": "healthcheck.periodic_ping",
  "pack_ref": "healthcheck",
  "trigger_ref": "core.interval_timer",
  "action_ref": "http.request",
  "action_params": {
    "method": "POST",
    "url": "{{ pack.config.healthcheck_endpoint }}",
    "headers": {
      "Authorization": "Bearer {{ pack.config.api_token }}",
      "Content-Type": "application/json"
    },
    "body": {
      "source": "attune",
      "timestamp": "{{ system.timestamp }}",
      "rule": "{{ system.rule.ref }}"
    },
    "timeout": 30
  }
}

Implementation Details

Template Processing Flow

  1. Rule Evaluation - When an event matches a rule
  2. Template Extraction - Identify {{ }} patterns in action_params
  3. Context Building - Assemble available data:
    • trigger.payload - Event payload data
    • pack.config - Pack configuration
    • system.* - System-provided values
  4. Value Resolution - Extract values from context using dot notation paths
  5. Type Conversion - Preserve JSON types (string, number, boolean, object, array)
  6. Parameter Assembly - Build final parameter object
  7. Enforcement Creation - Store resolved parameters in enforcement config
  8. Execution Creation - Pass parameters to action execution

Error Handling

Missing Values:

  • If a referenced value doesn't exist and no default is provided, use null
  • Log warning: "Template reference not found: trigger.payload.missing_field"

Invalid Syntax:

  • If template syntax is invalid, log error and use the raw string
  • Log error: "Invalid template syntax: {{ incomplete"

Type Mismatches:

  • Preserve JSON types when possible
  • Convert to string as fallback for complex interpolation

Configuration in Pack

Pack configuration should be stored securely and can include:

{
  "ref": "mypack",
  "config": {
    "api_token": "secret-token-here",
    "api_url": "https://api.example.com",
    "default_timeout": 30,
    "retry_attempts": 3,
    "enable_notifications": true,
    "notification_channels": ["#alerts", "#monitoring"]
  }
}

Security Note: Sensitive values (API keys, tokens, passwords) should be stored in pack config, not in rule definitions, since:

  • Pack configs can be encrypted
  • Easier to rotate credentials
  • Rules can be version controlled without exposing secrets

Best Practices

1. Use Pack Config for Secrets

Bad:

{
  "action_params": {
    "api_key": "sk_live_abc123xyz789"  // Hardcoded secret
  }
}

Good:

{
  "action_params": {
    "api_key": "{{ pack.config.api_key }}"
  }
}

2. Provide Defaults for Optional Fields

{
  "action_params": {
    "priority": "{{ trigger.payload.priority | default: 'medium' }}",
    "assignee": "{{ trigger.payload.assignee | default: 'unassigned' }}"
  }
}

3. Use Descriptive Template Paths

{
  "action_params": {
    "user_email": "{{ trigger.payload.user.email }}",
    "user_id": "{{ trigger.payload.user.id }}"
  }
}

4. Keep Static Values Where Appropriate

If a value never changes, keep it static:

{
  "action_params": {
    "service_name": "my-service",  // Static - never changes
    "error_code": "{{ trigger.payload.code }}"  // Dynamic - from event
  }
}

5. Test Your Templates

Create test events with sample payloads to verify your templates extract the correct values.


Testing Parameter Mapping

1. Manual Testing via API

Create a test event with known payload:

curl -X POST http://localhost:8080/api/v1/events \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "trigger_ref": "core.test_event",
    "payload": {
      "message": "Test message",
      "severity": "info",
      "user": {
        "id": 123,
        "name": "Alice"
      }
    }
  }'

Check the resulting enforcement and execution to verify parameters were resolved correctly:

# Check enforcement
curl -X GET http://localhost:8080/api/v1/enforcements/1 \
  -H "Authorization: Bearer $TOKEN"

# Check execution
curl -X GET http://localhost:8080/api/v1/executions/1 \
  -H "Authorization: Bearer $TOKEN"

2. Validate Parameter Resolution

Look for the resolved parameters in the execution's config field:

{
  "id": 1,
  "config": {
    "message": "Test message",       // Resolved from trigger.payload.message
    "severity": "info",              // Resolved from trigger.payload.severity
    "user_id": 123,                  // Resolved from trigger.payload.user.id
    "user_name": "Alice"             // Resolved from trigger.payload.user.name
  }
}

Migration Guide

From Static to Dynamic Parameters

Before (Static):

{
  "action_params": {
    "message": "An error occurred"
  }
}

After (Dynamic):

{
  "action_params": {
    "message": "Error: {{ trigger.payload.message }}"
  }
}

From Hardcoded Secrets to Pack Config

Before (Hardcoded):

{
  "action_params": {
    "api_key": "sk_live_abc123"
  }
}

Steps:

  1. Add secret to pack config
  2. Update rule to reference pack config
  3. Remove hardcoded value

After (Secure):

{
  "action_params": {
    "api_key": "{{ pack.config.api_key }}"
  }
}

Troubleshooting

Templates Not Resolving

Problem: Parameters contain literal {{ ... }} strings instead of resolved values.

Solutions:

  1. Check template syntax is correct
  2. Verify the referenced path exists in the event payload
  3. Check sensor service logs for template resolution errors
  4. Use default values for optional fields

Incorrect Values

Problem: Parameters have wrong values.

Solutions:

  1. Inspect event payload structure: SELECT payload FROM attune.event WHERE id = X;
  2. Verify the dot notation path matches the payload structure
  3. Check for typos in template paths
  4. Use system logs to see template resolution details

Type Conversion Issues

Problem: Numbers or booleans become strings.

Solutions:

  1. Ensure the source value is the correct type in the payload
  2. Check if string interpolation is converting types
  3. Use direct references without string interpolation for non-string types

Future Enhancements

1. Conditional Parameters

{
  "action_params": {
    "channel": "{% if trigger.payload.severity == 'critical' %}#incidents{% else %}#monitoring{% endif %}"
  }
}

2. Advanced Filters

  • Mathematical operations: {{ trigger.payload.value | multiply: 100 }}
  • String manipulation: {{ trigger.payload.text | replace: 'old', 'new' }}
  • Array operations: {{ trigger.payload.items | join: ', ' }}

3. Custom Functions

{
  "action_params": {
    "timestamp": "{{ now() }}",
    "uuid": "{{ uuid() }}",
    "hash": "{{ hash(trigger.payload.data) }}"
  }
}

4. Multi-Source Merging

{
  "action_params": {
    "user": "{{ trigger.payload.user | merge: pack.config.default_user }}"
  }
}


Summary

Rule parameter mapping provides a powerful way to:

  1. Decouple rules from data - Rules reference data locations, not specific values
  2. Reuse pack configuration - Share credentials and settings across rules
  3. Dynamic automation - Respond to events with context-aware actions
  4. Secure secrets - Store sensitive data in pack config, not rule definitions
  5. Flexible workflows - Build complex automations without custom code

Key Concepts:

  • Static values for constants
  • {{ trigger.payload.* }} for event data
  • {{ pack.config.* }} for pack configuration
  • {{ system.* }} for system-provided values
  • Filters and defaults for robust templates

This feature enables Attune to match the flexibility of platforms like StackStorm while maintaining a clean, declarative approach to automation.