式とテンプレート
式とテンプレートはProbeワークフローの動的な心臓部です。条件付きロジック、データ変換、動的設定を可能にします。このガイドでは、式システム、テンプレート構文、高度な使用パターンについて詳しく説明します。
式システム概要
Probeは2つのタイプの式を使用します:
- テンプレート式 (
{{expression}}) - 動的値の挿入用 - テスト式 (
expression) - ブール条件と検証用
両方とも、セキュリティ強化とカスタム関数を備えた expr ベースの同じ基礎式エンジンを使用しています。
テンプレート式
テンプレート式は{{}}構文を使用して文字列に動的値を挿入します。
基本テンプレート構文
# シンプルな変数置換
- name: Greet User
echo: "Hello {{vars.USERNAME}}!"
# ネストされたデータへのアクセス
- name: API Request
uses: http
with:
method: GET
url: "{{vars.API_BASE_URL}}/users/{{outputs.auth.user_id}}"
headers:
Authorization: "Bearer {{outputs.auth.access_token}}"
# 複雑な式
- name: Dynamic Configuration
echo: "Environment: {{vars.NODE_ENV || 'development'}}, Users: {{outputs.api.user_count || 0}}"テンプレート式コンテキスト
テンプレート式はいくつかのデータソースにアクセスできます:
環境変数 (env)
variables:
api_url: "{{vars.API_URL}}" # 環境変数
port: "{{vars.PORT || '3000'}}" # デフォルト値付き
debug_mode: "{{vars.DEBUG == 'true'}}" # ブール変換ステップ出力 (outputs)
steps:
- name: Get User Info
id: user-info
uses: http
with:
method: GET
url: "{{vars.API_URL}}/user/current"
outputs:
user_id: res.body.id
user_name: res.body.name
user_email: res.body.email
- name: Send Welcome Email
uses: smtp
with:
addr: "{{vars.smtp_addr}}"
from: "probe@example.com"
to: "{{outputs['user-info'].user_email}}"
subject: "Welcome {{outputs['user-info'].user_name}}!"
session: 1
message: 1
length: 500
echo: "Your user ID is: {{outputs['user-info'].user_id}}"ジョブ出力 (ジョブ間参照)
jobs:
- id: setup
name: setup
steps:
- name: Initialize
id: setup
outputs:
session_id: "{{random_str(16)}}"
- id: main-test
name: main-test
needs: [setup]
steps:
- name: Use Session
uses: http
with:
headers:
X-Session-ID: "{{outputs.setup.session_id}}"高度なテンプレートパターン
条件付き値
# 三項演算子
- name: Environment-specific URL
echo: "URL: {{vars.NODE_ENV == 'production' ? 'https://api.prod.com' : 'https://api.dev.com'}}"
# Null 合体
- name: Default Configuration
echo: "Timeout: {{vars.TIMEOUT || '30s'}}"文字列操作
# 文字列連結
- name: Build File Path
echo: "File: {{vars.BASE_PATH}}/{{vars.FILE_NAME}}.{{vars.FILE_EXT}}"
# 文字列メソッド(限定サポート)
- name: Format Output
echo: "User: {{upper(outputs.user.name)}} ({{lower(outputs.user.email)}})"算術演算
# 数学的演算
- name: Calculate Metrics
echo: |
Performance Metrics:
Average Response Time: {{(outputs.test1.time + outputs.test2.time + outputs.test3.time) / 3}}ms
Total Requests: {{outputs.test1.requests + outputs.test2.requests + outputs.test3.requests}}
Success Rate: {{(outputs.successful.count / outputs.total.count) * 100}}%複雑なデータアクセス
# 配列アクセス
- name: Process User List
echo: "First user: {{outputs.users.list[0].name}}"
# オブジェクトプロパティアクセス
- name: Nested Data Access
echo: "Database: {{outputs.config.database.host}}:{{outputs.config.database.port}}"テスト式
テスト式はtestとskipifで使うブール式です。
基本テスト構文
# シンプルなステータスチェック
- name: Health Check
uses: http
with:
method: GET
url: "{{vars.API_URL}}/health"
test: res.code == 200
# 複雑な条件
- name: Comprehensive API Test
uses: http
with:
method: GET
url: "{{vars.API_URL}}/api/data"
test: |
res.code == 200 &&
res.body.success == true &&
res.body.data != null &&
(rt.sec * 1000) < 1000HTTPレスポンステスト
resオブジェクトは包括的なレスポンスデータを提供します:
# ステータスコードテスト
test: res.code == 200
test: res.code >= 200 && res.code < 300
test: res.code in [200, 201, 202]
# レスポンス時間テスト
test: (rt.sec * 1000) < 1000 # 1秒未満
test: (rt.sec * 1000) >= 100 && (rt.sec * 1000) <= 500 # 100-500ms の間
# レスポンスサイズテスト
test: res.body_size > 0 # コンテンツあり
test: res.body_size < 1048576 # 1MB 未満
# ヘッダーテスト
test: res.headers["Content-Type"] == "application/json"
test: res.headers["X-Rate-Limit-Remaining"] > "10"
# JSON レスポンステスト
test: res.body.status == "success"
test: len(res.body.data.users) > 0
test: res.body.error == null
# テキストレスポンステスト
test: res.body contains "Success"
test: res.body startsWith "<!DOCTYPE html>"
test: len(res.body) > 100高度なテスト条件
正規表現
# レスポンステキストのパターンマッチング
test: res.body matches "user-\\d+@example\\.com"
# JSON フィールドパターン検証
test: res.body.user.email matches "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"配列とオブジェクトのテスト
# 配列テスト
test: len(res.body.users) == 5
test: res.body.tags contains "production"
test: all(res.body.permissions, #.active == true)
test: any(res.body.items, #.price > 100)
# オブジェクトプロパティテスト
test: "id" in res.body.user && "email" in res.body.user
test: res.body.config.database.host != null複雑な論理条件
# 複数条件検証
test: |
(res.code == 200 && res.body.success == true) ||
(res.code == 202 && res.body.processing == true)
# ネストした条件検証
test: |
res.code == 200 &&
res.body.data != null &&
(
(res.body.data.type == "user" && res.body.data.user.active == true) ||
(res.body.data.type == "system" && res.body.data.system.healthy == true)
)組み込み関数
Probeは一般的な操作のためのいくつかの組み込み関数を提供します。
ランダム関数
random_int(max)
ランダムな整数を生成:
# ランダムなユーザーID生成
- name: Create Test User
uses: http
with:
url: "{{vars.API_URL}}/users"
method: POST
body: |
{
"id": {{random_int(999999)}},
"name": "TestUser{{random_int(1000)}}",
"group": {{random_int(10)}}
}random_str(length)
ランダムな文字列を生成:
# ユニークな識別子を生成
- name: Create Session
outputs:
session_id: "session_{{random_str(16)}}"
transaction_id: "txn_{{random_str(12)}}"
correlation_id: "{{random_str(32)}}"
# テストデータ生成
- name: Create Test Record
uses: http
with:
body: |
{
"username": "user_{{random_str(8)}}",
"email": "test_{{random_str(6)}}@example.com",
"api_key": "{{random_str(40)}}"
}時間関数
unixtime()
現在のUnixタイムスタンプを取得:
# リクエストにタイムスタンプを追加
- name: Timestamped Request
uses: http
with:
url: "{{vars.API_URL}}/events"
method: POST
body: |
{
"event": "test_execution",
"timestamp": {{unixtime()}},
"execution_id": "exec_{{unixtime()}}_{{random_str(8)}}"
}
# 時間ベースのテスト
- name: Check Timestamp
uses: http
with:
method: GET
url: "{{vars.API_URL}}/status"
test: res.body.server_time >= {{unixtime() - 300}} # 過去5分以内カスタム関数使用パターン
ユニークテストデータ生成
jobs:
- id: user-lifecycle-test
name: user-lifecycle-test
steps:
- name: Create Unique User
id: create-user
uses: http
with:
url: "{{vars.API_URL}}/users"
method: POST
body: |
{
"username": "testuser_{{unixtime()}}_{{random_str(6)}}",
"email": "test_{{random_str(8)}}@example.com",
"password": "{{random_str(16)}}",
"user_id": {{random_int(1000000)}}
}
test: res.code == 201
outputs:
user_id: res.body.user.id
username: res.body.user.username
- name: Verify User Creation
uses: http
with:
method: GET
url: "{{vars.API_URL}}/users/{{outputs['create-user'].user_id}}"
test: |
res.code == 200 &&
res.body.user.username == "{{outputs['create-user'].username}}"
- name: Clean Up User
uses: http
with:
url: "{{vars.API_URL}}/users/{{outputs['create-user'].user_id}}"
method: DELETE
test: res.code == 204セッションと関連ID
jobs:
- id: distributed-trace-test
name: distributed-trace-test
steps:
- name: Initialize Trace
uses: hello
id: trace
echo: "Starting distributed trace"
outputs:
trace_id: "trace_{{unixtime()}}_{{random_str(16)}}"
correlation_id: "corr_{{random_str(32)}}"
- name: Service A Call
uses: http
with:
method: GET
url: "{{vars.SERVICE_A_URL}}/process"
headers:
X-Trace-ID: "{{outputs.trace.trace_id}}"
X-Correlation-ID: "{{outputs.trace.correlation_id}}"
test: res.code == 200
- name: Service B Call
uses: http
with:
method: GET
url: "{{vars.SERVICE_B_URL}}/process"
headers:
X-Trace-ID: "{{outputs.trace.trace_id}}"
X-Correlation-ID: "{{outputs.trace.correlation_id}}"
test: res.code == 200
- name: Verify Trace Correlation
uses: http
with:
method: GET
url: "{{vars.TRACING_URL}}/traces/{{outputs.trace.trace_id}}"
test: |
res.code == 200 &&
len(res.body.spans) >= 2 &&
res.body.correlation_id == "{{outputs.trace.correlation_id}}"条件付きロジックパターン
ステップレベル条件
ステップはskipifが真のときスキップされます。判断材料は先行ステップのoutputsとして公開しておきます。
steps:
- name: Check Primary Service
id: primary
uses: http
with:
method: GET
url: "{{vars.primary_url}}/health"
outputs:
primary_healthy: res.code == 200
- name: Check Secondary Service
id: secondary
uses: http
skipif: outputs.primary.primary_healthy
with:
method: GET
url: "{{vars.secondary_url}}/health"
outputs:
secondary_healthy: res.code == 200
- name: Success Path
uses: hello
skipif: "!(outputs.primary_healthy || (outputs.secondary_healthy ?? false))"
echo: "At least one service is healthy"
- name: Failure Path
uses: hello
skipif: outputs.primary_healthy || (outputs.secondary_healthy ?? false)
echo: "All services are down!"ジョブレベル条件
ジョブのskipifからはvarsと、依存しているジョブのoutputsを参照できます。
jobs:
- id: health-check
name: Health Check
steps:
- name: Basic Health Check
id: health
uses: http
with:
method: GET
url: "{{vars.api_url}}/health"
outputs:
healthy: res.code == 200
- name: Detailed Analysis
needs: [health-check]
skipif: outputs.health.healthy
steps:
- name: Deep Diagnostic
uses: http
with:
method: GET
url: "{{vars.api_url}}/diagnostics"
test: res.code == 200
- name: Performance Test
needs: [health-check]
skipif: "!outputs.health.healthy"
steps:
- name: Load Test
uses: http
with:
method: GET
url: "{{vars.api_url}}/load-test"
test: res.code == 200環境ベースの条件
steps:
- name: Development Setup
uses: hello
skipif: vars.node_env != "development"
echo: "Running in development mode"
- name: Production Validation
uses: http
skipif: vars.node_env != "production"
with:
method: GET
url: "{{vars.api_url}}/production-check"
test: res.code == 200
- name: Feature Flag Check
uses: http
skipif: "!(vars.feature_flags contains \"new-api\")"
with:
method: GET
url: "{{vars.api_url}}/v2/endpoint"
test: res.code == 200セキュリティ考慮事項
式のセキュリティ機能
Probeはいくつかのセキュリティ対策を実装しています:
- 式長制限: リソース枯渇を防止
- 危険な関数ブロック: システム関数へのアクセスをブロック
- 環境変数フィルタリング: 機密変数へのアクセスを制限
- タイムアウト保護: 式での無限ループを防止
安全な式パターン
# 良い例: 安全な環境変数アクセス
- name: Safe Config
echo: "API URL: {{vars.API_URL}}"
# 良い例: 制限されたデータアクセス
- name: Safe Data Access
test: len(res.body.users) <= 1000
# 避ける: 無制限の操作
# test: all(res.body.data.some_huge_array, expensive_operation(#))
# 良い例: シンプルな条件
- name: Simple Validation
test: res.code == 200 && res.body.success == true
# 避ける: 複雑なネスト式
# test: deeply.nested.complex.expression.with.many.operations()機密データの処理
# 良い例: シークレットに環境変数を使用
- name: Authenticated Request
uses: http
with:
headers:
Authorization: "Bearer {{vars.API_TOKEN}}"
# 良い例: 機密データのログを避ける
- name: Login Test
uses: http
with:
body: |
{
"username": "{{vars.TEST_USERNAME}}",
"password": "{{vars.TEST_PASSWORD}}"
}
# 機密レスポンスデータを出力しない
outputs:
login_successful: res.code == 200
# NG: auth_token: res.body.token (ログに露出する)パフォーマンス最適化
効率的な式の記述
# 良い例: シンプルで直接的な式
test: res.code == 200
# 良い例: && による早期終了
test: res.code == 200 && res.body.success == true
# 避ける: 式での複雑な計算
# test: expensive_calculation(res.body.large_dataset) == expected_value
# 良い例: 複雑な値を事前計算
outputs:
user_count: len(res.body.users)
active_users: len(filter(res.body.users, #.active == true))テンプレート最適化
# 良い例: シンプルなテンプレート置換
echo: "User {{outputs.user.name}} logged in"
# 良い例: 最小限の文字列操作
url: "{{vars.BASE_URL}}/users/{{outputs.user.id}}"
# 避ける: 複雑なテンプレート式
# echo: "{{complex_calculation(outputs.data) + another_operation(vars.CONFIG)}}"式のデバッグ
よくある問題と解決方法
テンプレート式エラー
# エラー: JSON でクォート不足
body: |
{
"name": {{outputs.user.name}} # エラー: クォートなし
}
# 解決方法: 適切な JSON クォート
body: |
{
"name": "{{outputs.user.name}}" # 正解: 文字列をクォート
}テスト式デバッグ
# 詳細モードでデバッグ
probe -v workflow.yml
# デバッグ出力を追加
- name: Debug Values
echo: |
Debug Information:
Status: {{res.code}}
Response Time: {{rt.duration}}
JSON Success: {{res.body.success}}
Headers: {{res.headers}}Null値の処理
# 良い例: 潜在的な null 値を処理
test: res.body.user != null && res.body.user.active == true
# 良い例: デフォルト値を使用
echo: "User count: {{outputs.api.user_count || 0}}"
# 良い例: アクセス前に存在をチェック
test: "data" in res.body && "users" in res.body.dataベストプラクティス
1. 式をシンプルに保つ
# 良い例: シンプルで読みやすい式
test: res.code == 200 && (rt.sec * 1000) < 1000
# 避ける: 過度に複雑な式
# test: (res.code >= 200 && res.code < 300) && ((rt.sec * 1000) < (vars.MAX_TIME || 1000)) && (len(filter(res.body.data.items, #.active && #.validated)) > 0)2. 意味のある変数名を使用
# 良い例: 説明的な出力名
outputs:
user_id: res.body.user.id
auth_token: res.body.access_token
expires_at: res.body.expires_in
# 避ける: 汎用的な名前
outputs:
data1: res.body.user.id
value: res.body.access_token3. エッジケースを処理
# 良い例: 防御的プログラミング
test: |
res.code == 200 &&
res.body != null &&
res.body.users != null &&
len(res.body.users) > 0
# 良い例: デフォルト値を提供
echo: "Processing {{outputs.api.item_count || 0}} items"4. 複雑な式を文書化
- name: Complex Business Logic Validation
uses: http
with:
method: GET
url: "{{vars.API_URL}}/business-data"
# テストは以下を検証:
# 1. レスポンスが成功 (200)
# 2. 処理時間が許容範囲 (< 2s)
# 3. データ整合性が維持 (必須フィールド存在)
# 4. ビジネスルールが満たされる (アクティブユーザー > 0、売上 > 閾値)
test: |
res.code == 200 &&
(rt.sec * 1000) < 2000 &&
res.body.users != null &&
res.body.revenue != null &&
len(filter(res.body.users, #.active == true)) > 0 &&
res.body.revenue > 1000次のステップ
式とテンプレートを理解したら、以下を探索してください:
- データフロー - ワークフローを通してデータがどのように移動するかを学ぶ
- テストとアサーション - 検証技術をマスターする
- ハウツー - 実用的な式使用パターンを見る
式とテンプレートはProbeの動的エンジンです。これらの概念をマスターして、柔軟でデータ駆動の自動化ワークフローを構築しましょう。
更新日時