6.1: n8n Workflow Patterns

Overview

n8n is an open-source, low-code workflow automation platform that excels at connecting development tools in ASPICE-compliant environments. This section covers practical patterns for requirements management, CI/CD, and development workflow automation.

Note: n8n workflow JSON examples are illustrative and may require adjustment for your n8n version and node configurations. Test workflows thoroughly before production use.


n8n Architecture

The following diagram shows n8n's event-driven architecture, illustrating how webhook triggers, scheduled polls, and manual triggers initiate ASPICE-compliant automation workflows.

n8n Workflow Triggers


Pattern 1: Requirements Sync Workflow

Use Case

Synchronize requirements between DOORS Next and Jira for cross-team collaboration. When a requirement is updated in DOORS, this workflow automatically creates or updates the corresponding Jira issue and notifies the team.

Workflow Visualization

The following diagram shows the complete Jira-DOORS synchronization workflow, illustrating how requirement change events flow through validation, transformation, and update nodes to maintain bidirectional consistency between the two systems.

Jira-DOORS Sync

Implementation: Step-by-Step

The workflow consists of 6 nodes, each handling a specific task. The following sections describe each component:

Step 1: Webhook Trigger

The workflow is triggered when DOORS Next sends a webhook notification about a requirement update.

{
  "name": "DOORS Webhook",
  "type": "n8n-nodes-base.webhook",
  "parameters": {
    "path": "doors-requirement-updated",
    "responseMode": "onReceived",
    "options": {}
  }
}

Step 2: Parse the Event Data

Extract the requirement ID, text, and status from the webhook payload. Build the DOORS URL for traceability.

{
  "name": "Parse DOORS Event",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const reqData = items[0].json;\nconst reqId = reqData.requirement.id;\nconst reqText = reqData.requirement.text;\nconst reqStatus = reqData.requirement.status;\n\nreturn {\n  json: {\n    requirementId: reqId,\n    text: reqText,\n    status: reqStatus,\n    doorsUrl: `https://doors.example.com/rm/requirements/${reqId}`\n  }\n};"
  }
}

Step 3: Check if Jira Issue Already Exists

Search Jira for an existing issue with a label matching the requirement ID. This determines whether we create a new issue or update an existing one.

{
  "name": "Check Jira Issue Exists",
  "type": "n8n-nodes-base.jira",
  "parameters": {
    "resource": "issue",
    "operation": "search",
    "jql": "project = REQ AND labels = {{ $json.requirementId }}",
    "returnAll": false,
    "limit": 1
  }
}

Step 4: Route to Create or Update

Based on the search result, branch the workflow. If no issue exists (total == 0), create a new one. Otherwise, update the existing issue.

{
  "name": "Route: Create or Update",
  "type": "n8n-nodes-base.if",
  "parameters": {
    "conditions": {
      "number": [{ "value1": "={{ $json.total }}", "operation": "equal", "value2": 0 }]
    }
  }
}

Step 5a: Create New Jira Issue (if not exists)

Create a new requirement issue in Jira with the requirement text, DOORS link, and auto-sync label.

{
  "name": "Create Jira Issue",
  "type": "n8n-nodes-base.jira",
  "parameters": {
    "resource": "issue",
    "operation": "create",
    "project": "REQ",
    "issueType": "Requirement",
    "summary": "={{ $node['Parse DOORS Event'].json.requirementId }}: {{ $node['Parse DOORS Event'].json.text.substring(0, 100) }}",
    "description": "{{ $node['Parse DOORS Event'].json.text }}\\n\\nDOORS Link: {{ $node['Parse DOORS Event'].json.doorsUrl }}",
    "labels": ["{{ $node['Parse DOORS Event'].json.requirementId }}", "auto-synced"]
  }
}

Step 5b: Update Existing Jira Issue (if exists)

Update the existing Jira issue with the latest requirement text and status from DOORS.

{
  "name": "Update Jira Issue",
  "type": "n8n-nodes-base.jira",
  "parameters": {
    "resource": "issue",
    "operation": "update",
    "issueKey": "={{ $node['Check Jira Issue Exists'].json.issues[0].key }}",
    "updateFields": {
      "description": "{{ $node['Parse DOORS Event'].json.text }}\\n\\nDOORS Link: {{ $node['Parse DOORS Event'].json.doorsUrl }}",
      "status": { "name": "={{ $node['Parse DOORS Event'].json.status }}" }
    }
  }
}

Step 6: Notify the Team

Send a Slack notification to inform the team about the sync. Includes requirement ID and status.

{
  "name": "Notify Team",
  "type": "n8n-nodes-base.slack",
  "parameters": {
    "channel": "#requirements-updates",
    "text": "Requirement {{ $node['Parse DOORS Event'].json.requirementId }} synced to Jira",
    "attachments": [{
      "color": "#0052CC",
      "fields": [
        { "title": "Requirement ID", "value": "={{ $node['Parse DOORS Event'].json.requirementId }}", "short": true },
        { "title": "Status", "value": "={{ $node['Parse DOORS Event'].json.status }}", "short": true }
      ]
    }]
  }
}

Node Connections

Define how nodes connect to form the workflow flow:

{
  "connections": {
    "DOORS Webhook": { "main": [[{ "node": "Parse DOORS Event", "type": "main", "index": 0 }]] },
    "Parse DOORS Event": { "main": [[{ "node": "Check Jira Issue Exists", "type": "main", "index": 0 }]] },
    "Check Jira Issue Exists": { "main": [[{ "node": "Route: Create or Update", "type": "main", "index": 0 }]] },
    "Route: Create or Update": { "main": [
      [{ "node": "Create Jira Issue", "type": "main", "index": 0 }],
      [{ "node": "Update Jira Issue", "type": "main", "index": 0 }]
    ]},
    "Create Jira Issue": { "main": [[{ "node": "Notify Team", "type": "main", "index": 0 }]] },
    "Update Jira Issue": { "main": [[{ "node": "Notify Team", "type": "main", "index": 0 }]] }
  }
}

What This Means: This pattern demonstrates bidirectional traceability (ASPICE SUP.8). Requirements flow from DOORS to Jira automatically, maintaining consistency across tools. The webhook trigger ensures real-time sync, while the create/update routing prevents duplicates.


Pattern 2: CI/CD Test Result Notification

Use Case

Monitor Jenkins builds and send intelligent notifications based on test results and AI-powered failure analysis. Critical failures trigger immediate alerts and automatic Jira ticket creation.

Workflow Visualization

The following diagram shows the CI/CD failure analysis workflow, illustrating how Jenkins build events are captured, test results are analyzed by an AI engine for root-cause classification, and notifications are routed to the appropriate teams based on severity.

CI Failure Analysis

Implementation: Step-by-Step

This workflow uses AI to analyze test failures and route notifications based on severity. The following steps describe each node:

Step 1: Get Jenkins Build Information

Retrieve build metadata when a build completes.

{
  "name": "Jenkins Build Complete",
  "type": "n8n-nodes-base.jenkins",
  "parameters": {
    "resource": "build",
    "operation": "get",
    "jobName": "{{ $json.jobName }}",
    "buildNumber": "{{ $json.buildNumber }}"
  }
}

Step 2: Extract Test Results

Fetch the detailed test report from Jenkins via its REST API.

{
  "name": "Extract Test Results",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "={{ $json.url }}testReport/api/json",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "jenkinsApi",
    "method": "GET"
  }
}

Step 3: AI-Powered Failure Analysis

Send failed test data to an AI service that analyzes patterns, identifies flaky tests, and suggests root causes.

{
  "name": "AI Failure Analysis",
  "type": "n8n-nodes-base.httpRequest",
  "parameters": {
    "url": "http://ai-service:5000/analyze-test-failures",
    "method": "POST",
    "bodyParameters": {
      "parameters": [
        { "name": "failures", "value": "={{ $json.failedTests }}" },
        { "name": "build_history", "value": "={{ $node['Get Build History'].json }}" }
      ]
    }
  }
}

Step 4: Determine Severity Level

Apply severity classification based on failure count and AI analysis. Flaky tests get lower severity.

{
  "name": "Determine Severity",
  "type": "n8n-nodes-base.function",
  "parameters": {
    "functionCode": "const failureCount = items[0].json.failCount;\nconst analysis = items[0].json.aiAnalysis;\n\nlet severity = 'low'; let color = '#36a64f';\nif (analysis.isFlaky) { severity = 'low'; color = '#ffcc00'; }\nelse if (failureCount > 10) { severity = 'critical'; color = '#ff0000'; }\nelse if (failureCount > 5) { severity = 'high'; color = '#ff6600'; }\nelse if (failureCount > 0) { severity = 'medium'; color = '#ffaa00'; }\n\nreturn { json: { ...items[0].json, severity, color } };"
  }
}
Condition Severity Color Action
AI detects flaky test Low Yellow Log only
10+ failures Critical Red Slack + Jira
5-10 failures High Orange Slack alert
1-5 failures Medium Amber Channel post

Step 5: Route by Severity

Switch node routes the workflow to appropriate notification handlers.

{
  "name": "Route by Severity",
  "type": "n8n-nodes-base.switch",
  "parameters": {
    "rules": [
      { "value": "critical", "outputIndex": 0 },
      { "value": "high", "outputIndex": 1 },
      { "value": "medium", "outputIndex": 2 },
      { "value": "low", "outputIndex": 3 }
    ]
  }
}

Step 6a: Critical Alert (Slack + Jira)

For critical failures, send an urgent Slack notification with @channel mention.

{
  "name": "Alert: Critical",
  "type": "n8n-nodes-base.slack",
  "parameters": {
    "channel": "#build-failures",
    "text": "@channel CRITICAL BUILD FAILURE",
    "attachments": [{
      "color": "={{ $json.color }}",
      "title": "Build #{{ $node['Jenkins Build Complete'].json.number }} FAILED",
      "title_link": "={{ $node['Jenkins Build Complete'].json.url }}",
      "fields": [
        { "title": "Failed Tests", "value": "={{ $json.failCount }}", "short": true },
        { "title": "AI Analysis", "value": "={{ $json.aiAnalysis.summary }}", "short": false }
      ]
    }]
  }
}

Step 6b: Auto-Create Jira Ticket for Critical Failures

Automatically create a high-priority bug ticket for critical build failures.

{
  "name": "Create Jira Ticket",
  "type": "n8n-nodes-base.jira",
  "parameters": {
    "resource": "issue",
    "operation": "create",
    "project": "BUILD",
    "issueType": "Bug",
    "priority": "Highest",
    "summary": "Critical Build Failure - Build #{{ $node['Jenkins Build Complete'].json.number }}",
    "description": "Build failed with {{ $json.failCount }} test failures.\\n\\nAI Analysis:\\n{{ $json.aiAnalysis.summary }}\\n\\nBuild URL: {{ $node['Jenkins Build Complete'].json.url }}"
  }
}

What This Means: This pattern combines ASPICE SUP.9 (Problem Resolution) with intelligent automation. The AI analysis distinguishes real failures from flaky tests, reducing alert fatigue. Critical failures automatically create trackable Jira tickets, ensuring issues aren't lost.


Pattern 3: Automated Traceability Update

Use Case

Automatically update traceability links when code commits reference requirements.

Python Custom Node

"""
Custom n8n node for traceability extraction from Git commits
"""

import re
from typing import List, Dict

class TraceabilityExtractor:
    def __init__(self):
        self.patterns = {
            'implements': r'Implements?:\s*((?:SWE|SYS|STKH)-\d+(?:\s*,\s*(?:SWE|SYS|STKH)-\d+)*)',
            'tests': r'Tests?:\s*((?:TC)-\d+(?:\s*,\s*TC-\d+)*)',
            'closes': r'Closes?:\s*((?:JIRA)-\d+(?:\s*,\s*JIRA-\d+)*)',
            'relates': r'Relates?:\s*((?:SWE|SYS|TC)-\d+(?:\s*,\s*(?:SWE|SYS|TC)-\d+)*)'
        }
    
    def extract_references(self, commit_message: str) -> Dict[str, List[str]]:
        """Extract requirement references from commit message."""
        references = {}
        
        for ref_type, pattern in self.patterns.items():
            matches = re.findall(pattern, commit_message, re.IGNORECASE)
            if matches:
                # Split comma-separated IDs and clean whitespace
                ids = []
                for match in matches:
                    ids.extend([id.strip() for id in match.split(',')])
                references[ref_type] = list(set(ids))  # Remove duplicates
        
        return references
    
    def create_trace_links(self, commit_hash: str, 
                          references: Dict[str, List[str]]) -> List[Dict]:
        """Create trace link records for database."""
        links = []
        
        for link_type, req_ids in references.items():
            for req_id in req_ids:
                links.append({
                    'source_type': 'commit',
                    'source_id': commit_hash,
                    'target_type': 'requirement',
                    'target_id': req_id,
                    'link_type': link_type,
                    'auto_created': True
                })
        
        return links

# n8n function node implementation
def process(items):
    """n8n function to process git commits and extract traceability."""
    extractor = TraceabilityExtractor()
    results = []
    
    for item in items:
        commit = item['json']
        commit_hash = commit.get('hash', commit.get('sha'))
        commit_message = commit.get('message', '')
        
        # Extract references
        references = extractor.extract_references(commit_message)
        
        if references:
            # Create trace links
            links = extractor.create_trace_links(commit_hash, references)
            
            results.append({
                'json': {
                    'commit_hash': commit_hash,
                    'commit_message': commit_message[:100],
                    'references': references,
                    'trace_links': links,
                    'link_count': len(links)
                }
            })
    
    return results

n8n Workflow Configuration

{
  "name": "Git Commit Traceability Sync",
  "nodes": [
    {
      "name": "Git Webhook",
      "type": "n8n-nodes-base.webhook",
      "parameters": {
        "path": "git-push",
        "responseMode": "onReceived"
      }
    },
    {
      "name": "Extract Commits",
      "type": "n8n-nodes-base.function",
      "parameters": {
        "functionCode": "return items[0].json.commits.map(commit => ({ json: commit }));"
      }
    },
    {
      "name": "Parse Traceability",
      "type": "n8n-nodes-base.functionItem",
      "parameters": {
        "functionCode": "// Use the TraceabilityExtractor code from above\nconst extractor = new TraceabilityExtractor();\nconst references = extractor.extract_references(item.message);\nconst links = extractor.create_trace_links(item.id, references);\n\nreturn {\n  commit: item,\n  references,\n  links\n};"
      }
    },
    {
      "name": "Update Neo4j",
      "type": "n8n-nodes-base.neo4j",
      "parameters": {
        "query": "UNWIND $links AS link\nMERGE (s:Commit {id: link.source_id})\nMERGE (t:Requirement {id: link.target_id})\nMERGE (s)-[r:IMPLEMENTS {auto_created: link.auto_created}]->(t)\nRETURN count(r) as created_links",
        "parameters": {
          "links": "={{ $json.links }}"
        }
      }
    },
    {
      "name": "Update DOORS",
      "type": "n8n-nodes-base.httpRequest",
      "parameters": {
        "url": "https://doors.example.com/api/requirements/{{ $json.target_id }}/links",
        "method": "POST",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "doorsApi",
        "body": {
          "link_type": "implemented_by",
          "target_id": "={{ $json.commit.id }}",
          "target_url": "https://github.com/org/repo/commit/{{ $json.commit.id }}"
        }
      }
    }
  ]
}

Summary

n8n Workflow Patterns for ASPICE development:

  • Low-Code Integration: Visual workflow designer for rapid automation
  • Extensible: Custom nodes for specialized logic
  • Tool Agnostic: Connects DOORS, Jira, Git, Jenkins, and more
  • Scalable: Handle high-volume events efficiently
  • Maintainable: Visual workflows are self-documenting

Best Practices:

  1. Use error handling and retry logic for external API calls
  2. Implement circuit breakers for unstable services
  3. Monitor workflow execution metrics
  4. Version control workflow JSON definitions
  5. Test workflows in staging before production deployment