QuadrastackQuadrastack
Documentation

Docs

Templating

Dynamic values with Go templates, Sprig functions, and template context

Templating

Use Go templates and Sprig functions to create dynamic values in your playbooks.

Overview

Quadrastack supports Go template syntax with the Sprig function library. Access variables, session data, and request context using template expressions, and transform values with built-in functions.

Basic Templating

requests:
  get-user:
    method: GET
    url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
    headers:
      Authorization: "Bearer {{.vars.token}}"

Template Syntax

Basic Expression

Use {{ and }} to delimit template expressions.

# Variable reference
url: "{{.vars.baseUrl}}/api"

# Function call
header: "{{uuidv4}}"

# Pipeline
timestamp: "{{now | date \"2006-01-02\"}}"

Template Context

Three main context objects are available:

  1. .vars - Variables from vars profiles
  2. .session - Session data from workflow steps
  3. .request - Current request context (headers, body, etc.)

Context: .vars

Access variables from your vars profiles.

Basic Variables

vars:
  default:
    baseUrl: https://api.example.com
    apiKey: test-key-123
    userId: 42

requests:
  get-user:
    method: GET
    url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
    headers:
      X-API-Key: "{{.vars.apiKey}}"

Nested Variables

vars:
  default:
    api:
      baseUrl: https://api.example.com
      version: v1
      timeout: 30
    user:
      name: John Doe
      email: john@example.com

requests:
  get-data:
    method: GET
    url: "{{.vars.api.baseUrl}}/{{.vars.api.version}}/data"
    body:
      userName: "{{.vars.user.name}}"
      userEmail: "{{.vars.user.email}}"

Array Variables

vars:
  default:
    userIds: [1, 2, 3, 4, 5]
    tags: [api, test, smoke]

requests:
  get-first-user:
    method: GET
    url: "{{.vars.baseUrl}}/users/{{index .vars.userIds 0}}"

  tagged-request:
    method: GET
    url: /api/data
    headers:
      X-Tags: "{{join \",\" .vars.tags}}"

Context: .session

Access response data from previous workflow steps.

Session Structure

When you capture a response with out in a workflow step:

workflows:
  auth-flow:
    steps:
      - request: login
        out: auth  # Captures response in .session.auth

The session object contains:

  • .session.<name>.status - HTTP status code
  • .session.<name>.headers.<HeaderName> - Response headers
  • .session.<name>.body.<jsonPath> - Response body (JSON)
  • .session.<name>.bodyRaw - Raw response body (string)

Accessing Status

workflows:
  check-flow:
    steps:
      - request: create-user
        out: created

      - request: verify-status
        expect:
          body:
            $.previousStatus: "{{.session.created.status}}"

Accessing Headers

workflows:
  header-flow:
    steps:
      - request: login
        out: auth

      - request: next-request
        headers:
          X-Session-ID: "{{.session.auth.headers.X-Session-ID}}"
          X-Rate-Limit: "{{.session.auth.headers.X-RateLimit-Remaining}}"

Accessing Body (JSON)

workflows:
  user-flow:
    steps:
      - request: login
        out: auth

      - request: get-profile
        url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}"
        headers:
          Authorization: "Bearer {{.session.auth.body.token}}"

      - request: update-profile
        body:
          userId: "{{.session.auth.body.userId}}"
          oldEmail: "{{.session.auth.body.user.email}}"

Accessing Raw Body

workflows:
  raw-flow:
    steps:
      - request: get-xml
        out: xmlResponse

      - request: send-raw
        body: "{{.session.xmlResponse.bodyRaw}}"

Nested JSON Paths

workflows:
  nested-flow:
    steps:
      - request: get-data
        out: data

      - request: use-nested
        body:
          # Access nested fields
          userName: "{{.session.data.body.user.profile.name}}"
          firstTag: "{{index .session.data.body.tags 0}}"
          metadata: "{{.session.data.body.metadata.timestamp}}"

Context: .request

Access current request context (less commonly used).

requests:
  echo-headers:
    method: POST
    url: /api/echo
    headers:
      X-Original-Path: "{{.request.path}}"

Note: .request context is primarily for advanced use cases. Most templates use .vars and .session.


Sprig Functions

Quadrastack includes the Sprig function library with 100+ template functions.

String Functions

uuidv4

Generate a random UUID v4.

headers:
  X-Request-ID: "{{uuidv4}}"

# Output: X-Request-ID: f47ac10b-58cc-4372-a567-0e02b2c3d479

upper / lower

Convert to uppercase/lowercase.

headers:
  X-Environment: "{{.vars.environment | upper}}"
  X-Name: "{{.vars.userName | lower}}"

# Input: environment: production, userName: JohnDoe
# Output: X-Environment: PRODUCTION, X-Name: johndoe

trim / trimSuffix / trimPrefix

Trim whitespace or specific strings.

body:
  cleanName: "{{.vars.userName | trim}}"
  noPrefix: "{{.vars.url | trimPrefix \"https://\"}}"
  noSuffix: "{{.vars.filename | trimSuffix \".json\"}}"

replace

Replace occurrences of a string.

body:
  sanitized: "{{.vars.text | replace \" \" \"_\"}}"

# Input: text: "hello world"
# Output: sanitized: "hello_world"

quote / squote

Add quotes around strings.

body:
  quoted: "{{.vars.value | quote}}"
  singleQuoted: "{{.vars.value | squote}}"

# Input: value: test
# Output: quoted: "test", singleQuoted: 'test'

contains / hasPrefix / hasSuffix

String matching.

# Used in assertions or conditionals
body:
  isProduction: "{{.vars.environment | hasPrefix \"prod\"}}"

Date Functions

now

Current timestamp.

headers:
  X-Timestamp: "{{now | date \"2006-01-02T15:04:05Z07:00\"}}"
  X-Unix: "{{now | unixEpoch}}"
  X-Date: "{{now | date \"2006-01-02\"}}"

# Output:
# X-Timestamp: 2024-01-15T10:30:00Z
# X-Unix: 1705318200
# X-Date: 2024-01-15

date

Format a timestamp.

Go date format: Use 2006-01-02 15:04:05 as reference

headers:
  # ISO 8601
  X-ISO: "{{now | date \"2006-01-02T15:04:05Z07:00\"}}"

  # US format
  X-US: "{{now | date \"01/02/2006\"}}"

  # Custom
  X-Custom: "{{now | date \"Mon Jan 2 15:04:05 MST 2006\"}}"

  # Time only
  X-Time: "{{now | date \"15:04:05\"}}"

unixEpoch

Unix timestamp (seconds since epoch).

body:
  timestamp: "{{now | unixEpoch}}"

# Output: timestamp: 1705318200

dateModify

Add or subtract time.

body:
  tomorrow: "{{now | dateModify \"24h\" | date \"2006-01-02\"}}"
  nextWeek: "{{now | dateModify \"168h\" | date \"2006-01-02\"}}"
  yesterday: "{{now | dateModify \"-24h\" | date \"2006-01-02\"}}"

Encoding Functions

b64enc / b64dec

Base64 encoding/decoding.

headers:
  Authorization: "Basic {{printf \"%s:%s\" .vars.username .vars.password | b64enc}}"

# Input: username: admin, password: secret
# Output: Authorization: Basic YWRtaW46c2VjcmV0

urlquery

URL encode a string.

requests:
  search:
    method: GET
    url: "/search?q={{.vars.searchTerm | urlquery}}"

# Input: searchTerm: "hello world"
# Output: /search?q=hello+world

List Functions

join

Join array elements with separator.

vars:
  default:
    tags: [api, test, smoke]

requests:
  tagged:
    method: GET
    url: /api/data
    headers:
      X-Tags: "{{join \",\" .vars.tags}}"

# Output: X-Tags: api,test,smoke

index

Get element at index (0-based).

vars:
  default:
    userIds: [10, 20, 30, 40]

requests:
  first-user:
    method: GET
    url: "/users/{{index .vars.userIds 0}}"

# Output: /users/10

Math Functions

add / sub / mul / div

Arithmetic operations.

body:
  sum: "{{add .vars.count 1}}"
  difference: "{{sub .vars.total .vars.used}}"
  product: "{{mul .vars.price .vars.quantity}}"
  quotient: "{{div .vars.total .vars.count}}"

max / min

Maximum/minimum value.

body:
  maximum: "{{max .vars.limit1 .vars.limit2}}"
  minimum: "{{min .vars.value1 .vars.value2}}"

Conditional Functions

default

Provide default value if empty.

body:
  name: "{{.vars.userName | default \"Anonymous\"}}"
  count: "{{.vars.itemCount | default \"0\"}}"

ternary

If-then-else expression.

body:
  environment: "{{ternary \"prod\" \"dev\" (eq .vars.env \"production\")}}"

# If .vars.env == "production": "prod"
# Otherwise: "dev"

Crypto Functions

sha256sum / sha1sum / md5sum

Generate hash digests.

headers:
  X-Content-Hash: "{{.vars.content | sha256sum}}"
  X-Checksum: "{{.vars.data | md5sum}}"

htpasswd

Generate htpasswd hash.

body:
  passwordHash: "{{htpasswd .vars.password}}"

Complete Examples

Authentication Flow

vars:
  default:
    baseUrl: https://api.example.com
    username: admin
    password: secret

workflows:
  auth-workflow:
    steps:
      # Step 1: Login
      - request: login
        out: auth

      # Step 2: Get user profile
      - request: get-profile
        out: profile

      # Step 3: Update settings
      - request: update-settings

requests:
  login:
    method: POST
    url: "{{.vars.baseUrl}}/auth/login"
    headers:
      X-Request-ID: "{{uuidv4}}"
      X-Timestamp: "{{now | unixEpoch}}"
    body:
      username: "{{.vars.username}}"
      password: "{{.vars.password}}"
      timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"

  get-profile:
    method: GET
    url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}"
    headers:
      Authorization: "Bearer {{.session.auth.body.token}}"
      X-Request-ID: "{{uuidv4}}"

  update-settings:
    method: PUT
    url: "{{.vars.baseUrl}}/users/{{.session.auth.body.userId}}/settings"
    headers:
      Authorization: "Bearer {{.session.auth.body.token}}"
    body:
      email: "{{.session.profile.body.email}}"
      name: "{{.session.profile.body.name | upper}}"
      updatedAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"

Dynamic Request Generation

vars:
  default:
    baseUrl: https://api.example.com
    environment: production
    version: 1.2.3

requests:
  dynamic-request:
    method: POST
    url: "{{.vars.baseUrl}}/api/v1/resources"
    headers:
      X-Request-ID: "{{uuidv4}}"
      X-Timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"
      X-Environment: "{{.vars.environment | upper}}"
      X-Version: "v{{.vars.version}}"
      X-Client: "quadrastack-{{.vars.version}}"
    body:
      id: "{{uuidv4}}"
      name: "Resource-{{now | date \"20060102-150405\"}}"
      environment: "{{.vars.environment}}"
      timestamp: "{{now | unixEpoch}}"
      createdAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"
      metadata:
        version: "{{.vars.version}}"
        hash: "{{.vars.version | sha256sum}}"

Complex Data Transformation

vars:
  default:
    baseUrl: https://api.example.com
    firstName: john
    lastName: doe
    tags: [api, production, critical]

workflows:
  complex-flow:
    steps:
      - request: create-resource
        out: created

      - request: update-resource

requests:
  create-resource:
    method: POST
    url: "{{.vars.baseUrl}}/resources"
    headers:
      X-Request-ID: "{{uuidv4}}"
    body:
      name: "{{.vars.firstName | title}} {{.vars.lastName | title}}"
      email: "{{.vars.firstName | lower}}.{{.vars.lastName | lower}}@example.com"
      username: "{{.vars.firstName | lower}}_{{.vars.lastName | lower}}_{{now | date \"20060102\"}}"
      tags: "{{join \",\" .vars.tags}}"
      hash: "{{printf \"%s-%s\" .vars.firstName .vars.lastName | sha256sum}}"

  update-resource:
    method: PUT
    url: "{{.vars.baseUrl}}/resources/{{.session.created.body.id}}"
    body:
      id: "{{.session.created.body.id}}"
      status: active
      updatedAt: "{{now | date \"2006-01-02T15:04:05Z\"}}"
      previousStatus: "{{.session.created.body.status}}"


Best Practices

1. Quote Template Expressions in YAML

Always quote template expressions to avoid YAML parsing issues:

Good:

url: "{{.vars.baseUrl}}/users"
header: "Bearer {{.vars.token}}"

Bad:

url: {{.vars.baseUrl}}/users  # YAML parsing error

2. Use Descriptive Variable Names

vars:
  default:
    apiBaseUrl: https://api.example.com  # Clear and descriptive
    userAuthToken: abc123
    requestTimeoutSeconds: 30

3. Validate Template Output

Use expect to validate template results:

requests:
  test-template:
    method: GET
    url: "{{.vars.baseUrl}}/users/{{.vars.userId}}"
    expect:
      status: 200

4. Use Default Values

Provide defaults for optional variables:

body:
  name: "{{.vars.userName | default \"Anonymous\"}}"
  timeout: "{{.vars.timeout | default \"30s\"}}"

5. Keep Templates Readable

Break complex templates across multiple fields:

Good:

body:
  userId: "{{.session.auth.body.userId}}"
  userName: "{{.session.auth.body.user.name}}"
  userEmail: "{{.session.auth.body.user.email}}"

Bad:

body:
  userData: "{{.session.auth.body.userId}}-{{.session.auth.body.user.name}}-{{.session.auth.body.user.email}}"

6. Use Consistent Date Formats

Pick a standard date format and stick to it:

# ISO 8601 recommended
timestamp: "{{now | date \"2006-01-02T15:04:05Z\"}}"

7. Document Complex Templates

vars:
  default:
    # Base URL for API (used in all requests)
    baseUrl: https://api.example.com

    # User ID for test scenarios (must be valid)
    userId: 123

Template Reference

Context Objects

ContextDescriptionExample
.vars.variableNameAccess variable from vars profile{{.vars.baseUrl}}
.vars.nested.fieldAccess nested variable{{.vars.api.version}}
.session.stepName.statusHTTP status from workflow step{{.session.auth.status}}
.session.stepName.headers.NameResponse header from step{{.session.auth.headers.Location}}
.session.stepName.body.fieldResponse body field from step{{.session.auth.body.token}}
.session.stepName.bodyRawRaw response body from step{{.session.auth.bodyRaw}}

Common Functions

FunctionDescriptionExample
uuidv4Generate UUID v4{{uuidv4}}
nowCurrent timestamp{{now}}
dateFormat timestamp{{now | date "2006-01-02"}}
unixEpochUnix timestamp{{now | unixEpoch}}
upperUppercase string{{.vars.name | upper}}
lowerLowercase string{{.vars.name | lower}}
trimTrim whitespace{{.vars.input | trim}}
replaceReplace substring{{.vars.text | replace " " "_"}}
b64encBase64 encode{{.vars.data | b64enc}}
b64decBase64 decode{{.vars.encoded | b64dec}}
sha256sumSHA-256 hash{{.vars.input | sha256sum}}
joinJoin array{{join "," .vars.tags}}
indexArray element{{index .vars.items 0}}
defaultDefault value{{.vars.name | default "Unknown"}}

See Sprig documentation for complete function reference.


Go Date Format Reference

Use these reference values for date formatting:

ComponentFormat
Year2006 (4-digit), 06 (2-digit)
Month01 (numeric), Jan (short), January (long)
Day02
Hour15 (24h), 03 (12h)
Minute04
Second05
TimezoneMST, Z07:00
Day of weekMon, Monday

Examples:

# ISO 8601: 2024-01-15T10:30:00Z
"{{now | date \"2006-01-02T15:04:05Z\"}}"

# US: 01/15/2024
"{{now | date \"01/02/2006\"}}"

# European: 15/01/2024
"{{now | date \"02/01/2006\"}}"

# Long: Monday, January 15, 2024
"{{now | date \"Monday, January 2, 2006\"}}"

# Time only: 10:30:05
"{{now | date \"15:04:05\"}}"

See Also

Templating - Quadrastack Docs