feat: Complete overhaul to enhanced documentation-only MCP server
- Removed all workflow execution capabilities per user requirements - Implemented enhanced documentation extraction with operations and API mappings - Fixed credential code extraction for all nodes - Fixed package info extraction (name and version) - Enhanced operations parser to handle n8n markdown format - Fixed documentation search to prioritize app nodes over trigger nodes - Comprehensive test coverage for Slack node extraction - All node information now includes: - Complete operations list (42 for Slack) - API method mappings with documentation URLs - Source code and credential definitions - Package metadata - Related resources and templates 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
51
tests/debug-slack-doc.js
Normal file
51
tests/debug-slack-doc.js
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const tempDir = path.join(process.cwd(), 'temp', 'n8n-docs');
|
||||
|
||||
console.log('🔍 Debugging Slack documentation search...\n');
|
||||
|
||||
// Search for all Slack related files
|
||||
console.log('All Slack-related markdown files:');
|
||||
try {
|
||||
const allSlackFiles = execSync(
|
||||
`find ${tempDir}/docs/integrations/builtin -name "*slack*.md" -type f`,
|
||||
{ encoding: 'utf-8' }
|
||||
).trim().split('\n');
|
||||
|
||||
allSlackFiles.forEach(file => {
|
||||
console.log(` - ${file}`);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(' No files found');
|
||||
}
|
||||
|
||||
console.log('\n📄 Checking file paths:');
|
||||
const possiblePaths = [
|
||||
'docs/integrations/builtin/app-nodes/n8n-nodes-base.Slack.md',
|
||||
'docs/integrations/builtin/app-nodes/n8n-nodes-base.slack.md',
|
||||
'docs/integrations/builtin/core-nodes/n8n-nodes-base.Slack.md',
|
||||
'docs/integrations/builtin/core-nodes/n8n-nodes-base.slack.md',
|
||||
'docs/integrations/builtin/trigger-nodes/n8n-nodes-base.Slack.md',
|
||||
'docs/integrations/builtin/trigger-nodes/n8n-nodes-base.slack.md',
|
||||
'docs/integrations/builtin/credentials/slack.md',
|
||||
];
|
||||
|
||||
const fs = require('fs');
|
||||
possiblePaths.forEach(p => {
|
||||
const fullPath = path.join(tempDir, p);
|
||||
const exists = fs.existsSync(fullPath);
|
||||
console.log(` ${exists ? '✓' : '✗'} ${p}`);
|
||||
|
||||
if (exists) {
|
||||
// Read first few lines
|
||||
const content = fs.readFileSync(fullPath, 'utf-8');
|
||||
const lines = content.split('\n').slice(0, 10);
|
||||
const title = lines.find(l => l.includes('title:'));
|
||||
if (title) {
|
||||
console.log(` Title: ${title.trim()}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
112
tests/demo-enhanced-documentation.js
Normal file
112
tests/demo-enhanced-documentation.js
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { EnhancedDocumentationFetcher } = require('../dist/utils/enhanced-documentation-fetcher');
|
||||
|
||||
async function demoEnhancedDocumentation() {
|
||||
console.log('=== Enhanced Documentation Parser Demo ===\n');
|
||||
console.log('This demo shows how the enhanced DocumentationFetcher extracts rich content from n8n documentation.\n');
|
||||
|
||||
const fetcher = new EnhancedDocumentationFetcher();
|
||||
|
||||
try {
|
||||
// Demo 1: Slack node (complex app node with many operations)
|
||||
console.log('1. SLACK NODE DOCUMENTATION');
|
||||
console.log('=' .repeat(50));
|
||||
const slackDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDoc) {
|
||||
console.log('\n📄 Basic Information:');
|
||||
console.log(` • Title: ${slackDoc.title}`);
|
||||
console.log(` • Description: ${slackDoc.description}`);
|
||||
console.log(` • URL: ${slackDoc.url}`);
|
||||
|
||||
console.log('\n📊 Content Statistics:');
|
||||
console.log(` • Operations: ${slackDoc.operations?.length || 0} operations across multiple resources`);
|
||||
console.log(` • API Methods: ${slackDoc.apiMethods?.length || 0} mapped to Slack API endpoints`);
|
||||
console.log(` • Examples: ${slackDoc.examples?.length || 0} code examples`);
|
||||
console.log(` • Resources: ${slackDoc.relatedResources?.length || 0} related documentation links`);
|
||||
console.log(` • Scopes: ${slackDoc.requiredScopes?.length || 0} OAuth scopes`);
|
||||
|
||||
// Show operations breakdown
|
||||
if (slackDoc.operations && slackDoc.operations.length > 0) {
|
||||
console.log('\n🔧 Operations by Resource:');
|
||||
const resourceMap = new Map();
|
||||
slackDoc.operations.forEach(op => {
|
||||
if (!resourceMap.has(op.resource)) {
|
||||
resourceMap.set(op.resource, []);
|
||||
}
|
||||
resourceMap.get(op.resource).push(op);
|
||||
});
|
||||
|
||||
for (const [resource, ops] of resourceMap) {
|
||||
console.log(`\n ${resource} (${ops.length} operations):`);
|
||||
ops.slice(0, 5).forEach(op => {
|
||||
console.log(` • ${op.operation}: ${op.description}`);
|
||||
});
|
||||
if (ops.length > 5) {
|
||||
console.log(` ... and ${ops.length - 5} more`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show API method mappings
|
||||
if (slackDoc.apiMethods && slackDoc.apiMethods.length > 0) {
|
||||
console.log('\n🔗 API Method Mappings (sample):');
|
||||
slackDoc.apiMethods.slice(0, 5).forEach(api => {
|
||||
console.log(` • ${api.resource}.${api.operation} → ${api.apiMethod}`);
|
||||
console.log(` URL: ${api.apiUrl}`);
|
||||
});
|
||||
if (slackDoc.apiMethods.length > 5) {
|
||||
console.log(` ... and ${slackDoc.apiMethods.length - 5} more mappings`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Demo 2: If node (core node with conditions)
|
||||
console.log('\n\n2. IF NODE DOCUMENTATION');
|
||||
console.log('=' .repeat(50));
|
||||
const ifDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.if');
|
||||
|
||||
if (ifDoc) {
|
||||
console.log('\n📄 Basic Information:');
|
||||
console.log(` • Title: ${ifDoc.title}`);
|
||||
console.log(` • Description: ${ifDoc.description}`);
|
||||
console.log(` • URL: ${ifDoc.url}`);
|
||||
|
||||
if (ifDoc.relatedResources && ifDoc.relatedResources.length > 0) {
|
||||
console.log('\n📚 Related Resources:');
|
||||
ifDoc.relatedResources.forEach(res => {
|
||||
console.log(` • ${res.title} (${res.type})`);
|
||||
console.log(` ${res.url}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Demo 3: Summary of enhanced parsing capabilities
|
||||
console.log('\n\n3. ENHANCED PARSING CAPABILITIES');
|
||||
console.log('=' .repeat(50));
|
||||
console.log('\nThe enhanced DocumentationFetcher can extract:');
|
||||
console.log(' ✓ Markdown frontmatter (metadata, tags, priority)');
|
||||
console.log(' ✓ Operations with resource grouping and descriptions');
|
||||
console.log(' ✓ API method mappings from markdown tables');
|
||||
console.log(' ✓ Code examples (JSON, JavaScript, YAML)');
|
||||
console.log(' ✓ Template references');
|
||||
console.log(' ✓ Related resources and documentation links');
|
||||
console.log(' ✓ Required OAuth scopes');
|
||||
console.log('\nThis rich content enables AI agents to:');
|
||||
console.log(' • Understand node capabilities in detail');
|
||||
console.log(' • Map operations to actual API endpoints');
|
||||
console.log(' • Provide accurate examples and usage patterns');
|
||||
console.log(' • Navigate related documentation');
|
||||
console.log(' • Understand authentication requirements');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\nError:', error);
|
||||
} finally {
|
||||
await fetcher.cleanup();
|
||||
console.log('\n\n✓ Demo completed');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the demo
|
||||
demoEnhancedDocumentation().catch(console.error);
|
||||
File diff suppressed because one or more lines are too long
94
tests/test-complete-fix.js
Executable file
94
tests/test-complete-fix.js
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { NodeDocumentationService } = require('../dist/services/node-documentation-service');
|
||||
|
||||
async function testCompleteFix() {
|
||||
console.log('=== Testing Complete Documentation Fix ===\n');
|
||||
|
||||
const service = new NodeDocumentationService('./data/test-nodes-v2.db');
|
||||
|
||||
try {
|
||||
// First check if we have any nodes
|
||||
const existingNodes = await service.listNodes();
|
||||
console.log(`📊 Current database has ${existingNodes.length} nodes`);
|
||||
|
||||
if (existingNodes.length === 0) {
|
||||
console.log('\n🔄 Rebuilding database with fixed documentation fetcher...');
|
||||
const stats = await service.rebuildDatabase();
|
||||
console.log(`\n✅ Rebuild complete:`);
|
||||
console.log(` - Total nodes found: ${stats.total}`);
|
||||
console.log(` - Successfully processed: ${stats.successful}`);
|
||||
console.log(` - Failed: ${stats.failed}`);
|
||||
|
||||
if (stats.errors.length > 0) {
|
||||
console.log('\n⚠️ Errors encountered:');
|
||||
stats.errors.slice(0, 5).forEach(err => console.log(` - ${err}`));
|
||||
}
|
||||
}
|
||||
|
||||
// Test specific nodes
|
||||
console.log('\n📋 Testing specific nodes:');
|
||||
|
||||
const testNodes = ['slack', 'if', 'httpRequest', 'webhook'];
|
||||
|
||||
for (const nodeName of testNodes) {
|
||||
const nodeInfo = await service.getNodeInfo(`n8n-nodes-base.${nodeName}`);
|
||||
|
||||
if (nodeInfo) {
|
||||
console.log(`\n✅ ${nodeInfo.displayName || nodeName}:`);
|
||||
console.log(` - Type: ${nodeInfo.nodeType}`);
|
||||
console.log(` - Description: ${nodeInfo.description?.substring(0, 80)}...`);
|
||||
console.log(` - Has source code: ${!!nodeInfo.sourceCode}`);
|
||||
console.log(` - Has documentation: ${!!nodeInfo.documentation}`);
|
||||
console.log(` - Documentation URL: ${nodeInfo.documentationUrl || 'N/A'}`);
|
||||
console.log(` - Has example: ${!!nodeInfo.exampleWorkflow}`);
|
||||
console.log(` - Category: ${nodeInfo.category || 'N/A'}`);
|
||||
|
||||
// Check if it's getting the right documentation
|
||||
if (nodeInfo.documentation) {
|
||||
const isCredentialDoc = nodeInfo.documentation.includes('credentials') &&
|
||||
!nodeInfo.documentation.includes('node documentation');
|
||||
console.log(` - Is credential doc: ${isCredentialDoc} ${isCredentialDoc ? '❌' : '✅'}`);
|
||||
}
|
||||
} else {
|
||||
console.log(`\n❌ ${nodeName}: Not found in database`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test search functionality
|
||||
console.log('\n🔍 Testing search functionality:');
|
||||
|
||||
const searchTests = [
|
||||
{ query: 'webhook', label: 'Webhook nodes' },
|
||||
{ query: 'http', label: 'HTTP nodes' },
|
||||
{ query: 'slack', label: 'Slack nodes' }
|
||||
];
|
||||
|
||||
for (const test of searchTests) {
|
||||
const results = await service.searchNodes({ query: test.query });
|
||||
console.log(`\n ${test.label}: ${results.length} results`);
|
||||
results.slice(0, 3).forEach(node => {
|
||||
console.log(` - ${node.displayName} (${node.nodeType})`);
|
||||
});
|
||||
}
|
||||
|
||||
// Get final statistics
|
||||
console.log('\n📊 Final database statistics:');
|
||||
const stats = service.getStatistics();
|
||||
console.log(` - Total nodes: ${stats.totalNodes}`);
|
||||
console.log(` - Nodes with documentation: ${stats.nodesWithDocs}`);
|
||||
console.log(` - Nodes with examples: ${stats.nodesWithExamples}`);
|
||||
console.log(` - Trigger nodes: ${stats.triggerNodes}`);
|
||||
console.log(` - Webhook nodes: ${stats.webhookNodes}`);
|
||||
|
||||
console.log('\n✅ All tests completed!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test failed:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
service.close();
|
||||
}
|
||||
}
|
||||
|
||||
testCompleteFix().catch(console.error);
|
||||
38
tests/test-debug-enhanced.js
Normal file
38
tests/test-debug-enhanced.js
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { EnhancedDocumentationFetcher } = require('../dist/utils/enhanced-documentation-fetcher');
|
||||
|
||||
async function debugTest() {
|
||||
console.log('=== Debug Enhanced Documentation ===\n');
|
||||
|
||||
const fetcher = new EnhancedDocumentationFetcher();
|
||||
|
||||
try {
|
||||
await fetcher.ensureDocsRepository();
|
||||
|
||||
// Test Slack documentation parsing
|
||||
console.log('Testing Slack documentation...');
|
||||
const slackDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDoc) {
|
||||
console.log('\nSlack Documentation:');
|
||||
console.log('- Operations found:', slackDoc.operations?.length || 0);
|
||||
|
||||
// Show raw markdown around operations section
|
||||
const operationsIndex = slackDoc.markdown.indexOf('## Operations');
|
||||
if (operationsIndex > -1) {
|
||||
console.log('\nRaw markdown around Operations section:');
|
||||
console.log('---');
|
||||
console.log(slackDoc.markdown.substring(operationsIndex, operationsIndex + 1000));
|
||||
console.log('---');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
} finally {
|
||||
await fetcher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
debugTest().catch(console.error);
|
||||
57
tests/test-docs-fix.js
Executable file
57
tests/test-docs-fix.js
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { DocumentationFetcher } = require('../dist/utils/documentation-fetcher');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
|
||||
async function testDocsFix() {
|
||||
console.log('=== Testing Documentation Fix ===\n');
|
||||
|
||||
const docsFetcher = new DocumentationFetcher();
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
try {
|
||||
// Test nodes
|
||||
const testNodes = [
|
||||
'n8n-nodes-base.slack',
|
||||
'n8n-nodes-base.if',
|
||||
'n8n-nodes-base.httpRequest',
|
||||
'n8n-nodes-base.webhook'
|
||||
];
|
||||
|
||||
for (const nodeType of testNodes) {
|
||||
console.log(`\n📋 Testing ${nodeType}:`);
|
||||
|
||||
// Test documentation fetching
|
||||
const docs = await docsFetcher.getNodeDocumentation(nodeType);
|
||||
if (docs) {
|
||||
console.log(` ✅ Documentation found`);
|
||||
console.log(` 📄 URL: ${docs.url}`);
|
||||
const titleMatch = docs.markdown.match(/title:\s*(.+)/);
|
||||
if (titleMatch) {
|
||||
console.log(` 📝 Title: ${titleMatch[1]}`);
|
||||
}
|
||||
console.log(` 📏 Length: ${docs.markdown.length} characters`);
|
||||
console.log(` 🔧 Has examples: ${docs.examples && docs.examples.length > 0}`);
|
||||
} else {
|
||||
console.log(` ❌ No documentation found`);
|
||||
}
|
||||
|
||||
// Test source extraction
|
||||
try {
|
||||
const source = await extractor.extractNodeSource(nodeType);
|
||||
console.log(` ✅ Source code found at: ${source.location}`);
|
||||
} catch (error) {
|
||||
console.log(` ❌ Source extraction failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Test completed!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test failed:', error);
|
||||
} finally {
|
||||
await docsFetcher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
testDocsFix().catch(console.error);
|
||||
141
tests/test-enhanced-documentation.js
Normal file
141
tests/test-enhanced-documentation.js
Normal file
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { EnhancedDocumentationFetcher } = require('../dist/utils/enhanced-documentation-fetcher');
|
||||
const { EnhancedSQLiteStorageService } = require('../dist/services/enhanced-sqlite-storage-service');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
|
||||
async function testEnhancedDocumentation() {
|
||||
console.log('=== Testing Enhanced Documentation Fetcher ===\n');
|
||||
|
||||
const fetcher = new EnhancedDocumentationFetcher();
|
||||
const storage = new EnhancedSQLiteStorageService('./data/test-enhanced.db');
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
try {
|
||||
// Test 1: Fetch and parse Slack node documentation
|
||||
console.log('1. Testing Slack node documentation parsing...');
|
||||
const slackDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDoc) {
|
||||
console.log('\n✓ Slack Documentation Found:');
|
||||
console.log(` - Title: ${slackDoc.title}`);
|
||||
console.log(` - Description: ${slackDoc.description}`);
|
||||
console.log(` - URL: ${slackDoc.url}`);
|
||||
console.log(` - Operations: ${slackDoc.operations?.length || 0} found`);
|
||||
console.log(` - API Methods: ${slackDoc.apiMethods?.length || 0} found`);
|
||||
console.log(` - Examples: ${slackDoc.examples?.length || 0} found`);
|
||||
console.log(` - Required Scopes: ${slackDoc.requiredScopes?.length || 0} found`);
|
||||
|
||||
// Show sample operations
|
||||
if (slackDoc.operations && slackDoc.operations.length > 0) {
|
||||
console.log('\n Sample Operations:');
|
||||
slackDoc.operations.slice(0, 5).forEach(op => {
|
||||
console.log(` - ${op.resource}.${op.operation}: ${op.description}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Show sample API mappings
|
||||
if (slackDoc.apiMethods && slackDoc.apiMethods.length > 0) {
|
||||
console.log('\n Sample API Mappings:');
|
||||
slackDoc.apiMethods.slice(0, 5).forEach(api => {
|
||||
console.log(` - ${api.resource}.${api.operation} → ${api.apiMethod}`);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('✗ Slack documentation not found');
|
||||
}
|
||||
|
||||
// Test 2: Test with If node (core node)
|
||||
console.log('\n\n2. Testing If node documentation parsing...');
|
||||
const ifDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.if');
|
||||
|
||||
if (ifDoc) {
|
||||
console.log('\n✓ If Documentation Found:');
|
||||
console.log(` - Title: ${ifDoc.title}`);
|
||||
console.log(` - Description: ${ifDoc.description}`);
|
||||
console.log(` - Examples: ${ifDoc.examples?.length || 0} found`);
|
||||
console.log(` - Related Resources: ${ifDoc.relatedResources?.length || 0} found`);
|
||||
}
|
||||
|
||||
// Test 3: Store node with documentation
|
||||
console.log('\n\n3. Testing node storage with documentation...');
|
||||
|
||||
// Extract a node
|
||||
const nodeInfo = await extractor.extractNodeSource('n8n-nodes-base.slack');
|
||||
if (nodeInfo) {
|
||||
const storedNode = await storage.storeNodeWithDocumentation(nodeInfo);
|
||||
|
||||
console.log('\n✓ Node stored successfully:');
|
||||
console.log(` - Node Type: ${storedNode.nodeType}`);
|
||||
console.log(` - Has Documentation: ${!!storedNode.documentationMarkdown}`);
|
||||
console.log(` - Operations: ${storedNode.operationCount}`);
|
||||
console.log(` - API Methods: ${storedNode.apiMethodCount}`);
|
||||
console.log(` - Examples: ${storedNode.exampleCount}`);
|
||||
console.log(` - Resources: ${storedNode.resourceCount}`);
|
||||
console.log(` - Scopes: ${storedNode.scopeCount}`);
|
||||
|
||||
// Get detailed operations
|
||||
const operations = await storage.getNodeOperations(storedNode.id);
|
||||
if (operations.length > 0) {
|
||||
console.log('\n Stored Operations (first 5):');
|
||||
operations.slice(0, 5).forEach(op => {
|
||||
console.log(` - ${op.resource}.${op.operation}: ${op.description}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Get examples
|
||||
const examples = await storage.getNodeExamples(storedNode.id);
|
||||
if (examples.length > 0) {
|
||||
console.log('\n Stored Examples:');
|
||||
examples.forEach(ex => {
|
||||
console.log(` - ${ex.title || 'Untitled'} (${ex.type}): ${ex.code.length} chars`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Test 4: Search with enhanced FTS
|
||||
console.log('\n\n4. Testing enhanced search...');
|
||||
|
||||
const searchResults = await storage.searchNodes({ query: 'slack message' });
|
||||
console.log(`\n✓ Search Results for "slack message": ${searchResults.length} nodes found`);
|
||||
|
||||
if (searchResults.length > 0) {
|
||||
console.log(' First result:');
|
||||
const result = searchResults[0];
|
||||
console.log(` - ${result.displayName || result.name} (${result.nodeType})`);
|
||||
console.log(` - Documentation: ${result.documentationTitle || 'No title'}`);
|
||||
}
|
||||
|
||||
// Test 5: Get statistics
|
||||
console.log('\n\n5. Getting enhanced statistics...');
|
||||
const stats = await storage.getEnhancedStatistics();
|
||||
|
||||
console.log('\n✓ Enhanced Statistics:');
|
||||
console.log(` - Total Nodes: ${stats.totalNodes}`);
|
||||
console.log(` - Nodes with Documentation: ${stats.nodesWithDocumentation}`);
|
||||
console.log(` - Documentation Coverage: ${stats.documentationCoverage}%`);
|
||||
console.log(` - Total Operations: ${stats.totalOperations}`);
|
||||
console.log(` - Total API Methods: ${stats.totalApiMethods}`);
|
||||
console.log(` - Total Examples: ${stats.totalExamples}`);
|
||||
console.log(` - Total Resources: ${stats.totalResources}`);
|
||||
console.log(` - Total Scopes: ${stats.totalScopes}`);
|
||||
|
||||
if (stats.topDocumentedNodes && stats.topDocumentedNodes.length > 0) {
|
||||
console.log('\n Top Documented Nodes:');
|
||||
stats.topDocumentedNodes.slice(0, 3).forEach(node => {
|
||||
console.log(` - ${node.display_name || node.name}: ${node.operation_count} operations, ${node.example_count} examples`);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error during testing:', error);
|
||||
} finally {
|
||||
// Cleanup
|
||||
storage.close();
|
||||
await fetcher.cleanup();
|
||||
console.log('\n\n✓ Test completed and cleaned up');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testEnhancedDocumentation().catch(console.error);
|
||||
156
tests/test-enhanced-final.js
Normal file
156
tests/test-enhanced-final.js
Normal file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { EnhancedDocumentationFetcher } = require('../dist/utils/enhanced-documentation-fetcher');
|
||||
const { EnhancedSQLiteStorageService } = require('../dist/services/enhanced-sqlite-storage-service');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
|
||||
async function testEnhancedDocumentation() {
|
||||
console.log('=== Enhanced Documentation Parser Test ===\n');
|
||||
|
||||
const fetcher = new EnhancedDocumentationFetcher();
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
try {
|
||||
// Test 1: Parse Slack documentation
|
||||
console.log('1. Parsing Slack node documentation...');
|
||||
const slackDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDoc) {
|
||||
console.log('\n✓ Slack Documentation Parsed:');
|
||||
console.log(` Title: ${slackDoc.title}`);
|
||||
console.log(` Description: ${slackDoc.description?.substring(0, 100)}...`);
|
||||
console.log(` URL: ${slackDoc.url}`);
|
||||
console.log(` Operations: ${slackDoc.operations?.length || 0} found`);
|
||||
console.log(` API Methods: ${slackDoc.apiMethods?.length || 0} found`);
|
||||
console.log(` Related Resources: ${slackDoc.relatedResources?.length || 0} found`);
|
||||
|
||||
// Show sample operations
|
||||
if (slackDoc.operations && slackDoc.operations.length > 0) {
|
||||
console.log('\n Sample Operations (first 10):');
|
||||
slackDoc.operations.slice(0, 10).forEach((op, i) => {
|
||||
console.log(` ${i + 1}. ${op.resource}.${op.operation}: ${op.description}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Show sample API mappings
|
||||
if (slackDoc.apiMethods && slackDoc.apiMethods.length > 0) {
|
||||
console.log('\n Sample API Method Mappings (first 5):');
|
||||
slackDoc.apiMethods.slice(0, 5).forEach((api, i) => {
|
||||
console.log(` ${i + 1}. ${api.resource}.${api.operation} → ${api.apiMethod} (${api.apiUrl})`);
|
||||
});
|
||||
}
|
||||
|
||||
// Show related resources
|
||||
if (slackDoc.relatedResources && slackDoc.relatedResources.length > 0) {
|
||||
console.log('\n Related Resources:');
|
||||
slackDoc.relatedResources.forEach((res, i) => {
|
||||
console.log(` ${i + 1}. ${res.title} (${res.type}): ${res.url}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2: Parse HTTP Request documentation (if available)
|
||||
console.log('\n\n2. Parsing HTTP Request node documentation...');
|
||||
const httpDoc = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.httpRequest');
|
||||
|
||||
if (httpDoc) {
|
||||
console.log('\n✓ HTTP Request Documentation Parsed:');
|
||||
console.log(` Title: ${httpDoc.title}`);
|
||||
console.log(` Examples: ${httpDoc.examples?.length || 0} found`);
|
||||
|
||||
if (httpDoc.examples && httpDoc.examples.length > 0) {
|
||||
console.log('\n Code Examples:');
|
||||
httpDoc.examples.forEach((ex, i) => {
|
||||
console.log(` ${i + 1}. ${ex.title || 'Example'} (${ex.type}): ${ex.code.length} characters`);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log(' HTTP Request documentation not found');
|
||||
}
|
||||
|
||||
// Test 3: Database storage test with smaller database
|
||||
console.log('\n\n3. Testing enhanced database storage...');
|
||||
const storage = new EnhancedSQLiteStorageService('./data/demo-enhanced.db');
|
||||
|
||||
try {
|
||||
// Store Slack node with documentation
|
||||
const slackNodeInfo = await extractor.extractNodeSource('n8n-nodes-base.slack');
|
||||
if (slackNodeInfo) {
|
||||
const storedNode = await storage.storeNodeWithDocumentation(slackNodeInfo);
|
||||
|
||||
console.log('\n✓ Slack node stored with documentation:');
|
||||
console.log(` Node Type: ${storedNode.nodeType}`);
|
||||
console.log(` Documentation: ${storedNode.documentationTitle || 'No title'}`);
|
||||
console.log(` Operations stored: ${storedNode.operationCount}`);
|
||||
console.log(` API methods stored: ${storedNode.apiMethodCount}`);
|
||||
console.log(` Examples stored: ${storedNode.exampleCount}`);
|
||||
console.log(` Resources stored: ${storedNode.resourceCount}`);
|
||||
}
|
||||
|
||||
// Store a few more nodes
|
||||
const nodeTypes = ['n8n-nodes-base.if', 'n8n-nodes-base.webhook'];
|
||||
for (const nodeType of nodeTypes) {
|
||||
try {
|
||||
const nodeInfo = await extractor.extractNodeSource(nodeType);
|
||||
if (nodeInfo) {
|
||||
await storage.storeNodeWithDocumentation(nodeInfo);
|
||||
console.log(` ✓ Stored ${nodeType}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(` ✗ Failed to store ${nodeType}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test search functionality
|
||||
console.log('\n\n4. Testing enhanced search...');
|
||||
|
||||
const searchTests = [
|
||||
{ query: 'slack', description: 'Search for "slack"' },
|
||||
{ query: 'message send', description: 'Search for "message send"' },
|
||||
{ query: 'webhook', description: 'Search for "webhook"' }
|
||||
];
|
||||
|
||||
for (const test of searchTests) {
|
||||
const results = await storage.searchNodes({ query: test.query });
|
||||
console.log(`\n ${test.description}: ${results.length} results`);
|
||||
if (results.length > 0) {
|
||||
const first = results[0];
|
||||
console.log(` Top result: ${first.displayName || first.name} (${first.nodeType})`);
|
||||
if (first.documentationTitle) {
|
||||
console.log(` Documentation: ${first.documentationTitle}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get final statistics
|
||||
console.log('\n\n5. Database Statistics:');
|
||||
const stats = await storage.getEnhancedStatistics();
|
||||
|
||||
console.log(` Total Nodes: ${stats.totalNodes}`);
|
||||
console.log(` Nodes with Documentation: ${stats.nodesWithDocumentation} (${stats.documentationCoverage}% coverage)`);
|
||||
console.log(` Total Operations: ${stats.totalOperations}`);
|
||||
console.log(` Total API Methods: ${stats.totalApiMethods}`);
|
||||
console.log(` Total Examples: ${stats.totalExamples}`);
|
||||
console.log(` Total Resources: ${stats.totalResources}`);
|
||||
|
||||
if (stats.topDocumentedNodes && stats.topDocumentedNodes.length > 0) {
|
||||
console.log('\n Best Documented Nodes:');
|
||||
stats.topDocumentedNodes.forEach((node, i) => {
|
||||
console.log(` ${i + 1}. ${node.display_name || node.name}: ${node.operation_count} operations, ${node.example_count} examples`);
|
||||
});
|
||||
}
|
||||
|
||||
} finally {
|
||||
storage.close();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\nError:', error);
|
||||
} finally {
|
||||
await fetcher.cleanup();
|
||||
console.log('\n\n✓ Test completed and cleaned up');
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testEnhancedDocumentation().catch(console.error);
|
||||
163
tests/test-enhanced-integration.js
Normal file
163
tests/test-enhanced-integration.js
Normal file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { DocumentationFetcher } = require('../dist/utils/documentation-fetcher');
|
||||
const { NodeDocumentationService } = require('../dist/services/node-documentation-service');
|
||||
|
||||
async function testEnhancedIntegration() {
|
||||
console.log('🧪 Testing Enhanced Documentation Integration...\n');
|
||||
|
||||
// Test 1: DocumentationFetcher backward compatibility
|
||||
console.log('1️⃣ Testing DocumentationFetcher backward compatibility...');
|
||||
const docFetcher = new DocumentationFetcher();
|
||||
|
||||
try {
|
||||
// Test getNodeDocumentation (backward compatible method)
|
||||
const simpleDoc = await docFetcher.getNodeDocumentation('n8n-nodes-base.slack');
|
||||
if (simpleDoc) {
|
||||
console.log(' ✅ Simple documentation format works');
|
||||
console.log(` - Has markdown: ${!!simpleDoc.markdown}`);
|
||||
console.log(` - Has URL: ${!!simpleDoc.url}`);
|
||||
console.log(` - Has examples: ${simpleDoc.examples?.length || 0}`);
|
||||
}
|
||||
|
||||
// Test getEnhancedNodeDocumentation (new method)
|
||||
const enhancedDoc = await docFetcher.getEnhancedNodeDocumentation('n8n-nodes-base.slack');
|
||||
if (enhancedDoc) {
|
||||
console.log(' ✅ Enhanced documentation format works');
|
||||
console.log(` - Title: ${enhancedDoc.title || 'N/A'}`);
|
||||
console.log(` - Operations: ${enhancedDoc.operations?.length || 0}`);
|
||||
console.log(` - API Methods: ${enhancedDoc.apiMethods?.length || 0}`);
|
||||
console.log(` - Examples: ${enhancedDoc.examples?.length || 0}`);
|
||||
console.log(` - Templates: ${enhancedDoc.templates?.length || 0}`);
|
||||
console.log(` - Related Resources: ${enhancedDoc.relatedResources?.length || 0}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(' ❌ DocumentationFetcher test failed:', error.message);
|
||||
}
|
||||
|
||||
// Test 2: NodeDocumentationService with enhanced fields
|
||||
console.log('\n2️⃣ Testing NodeDocumentationService enhanced schema...');
|
||||
const docService = new NodeDocumentationService('data/test-enhanced-docs.db');
|
||||
|
||||
try {
|
||||
// Store a test node with enhanced documentation
|
||||
const testNode = {
|
||||
nodeType: 'test.enhanced-node',
|
||||
name: 'enhanced-node',
|
||||
displayName: 'Enhanced Test Node',
|
||||
description: 'A test node with enhanced documentation',
|
||||
sourceCode: 'const testCode = "example";',
|
||||
packageName: 'test-package',
|
||||
documentation: '# Test Documentation',
|
||||
documentationUrl: 'https://example.com/docs',
|
||||
documentationTitle: 'Enhanced Test Node Documentation',
|
||||
operations: [
|
||||
{
|
||||
resource: 'Message',
|
||||
operation: 'Send',
|
||||
description: 'Send a message'
|
||||
}
|
||||
],
|
||||
apiMethods: [
|
||||
{
|
||||
resource: 'Message',
|
||||
operation: 'Send',
|
||||
apiMethod: 'chat.postMessage',
|
||||
apiUrl: 'https://api.slack.com/methods/chat.postMessage'
|
||||
}
|
||||
],
|
||||
documentationExamples: [
|
||||
{
|
||||
title: 'Send Message Example',
|
||||
type: 'json',
|
||||
code: '{"text": "Hello World"}'
|
||||
}
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
name: 'Basic Message Template',
|
||||
description: 'Simple message sending template'
|
||||
}
|
||||
],
|
||||
relatedResources: [
|
||||
{
|
||||
title: 'API Documentation',
|
||||
url: 'https://api.slack.com',
|
||||
type: 'api'
|
||||
}
|
||||
],
|
||||
requiredScopes: ['chat:write'],
|
||||
hasCredentials: true,
|
||||
isTrigger: false,
|
||||
isWebhook: false
|
||||
};
|
||||
|
||||
await docService.storeNode(testNode);
|
||||
console.log(' ✅ Stored node with enhanced documentation');
|
||||
|
||||
// Retrieve and verify
|
||||
const retrieved = await docService.getNodeInfo('test.enhanced-node');
|
||||
if (retrieved) {
|
||||
console.log(' ✅ Retrieved node with enhanced fields:');
|
||||
console.log(` - Has operations: ${!!retrieved.operations}`);
|
||||
console.log(` - Has API methods: ${!!retrieved.apiMethods}`);
|
||||
console.log(` - Has documentation examples: ${!!retrieved.documentationExamples}`);
|
||||
console.log(` - Has templates: ${!!retrieved.templates}`);
|
||||
console.log(` - Has related resources: ${!!retrieved.relatedResources}`);
|
||||
console.log(` - Has required scopes: ${!!retrieved.requiredScopes}`);
|
||||
}
|
||||
|
||||
// Test search
|
||||
const searchResults = await docService.searchNodes({ query: 'enhanced' });
|
||||
console.log(` ✅ Search found ${searchResults.length} results`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(' ❌ NodeDocumentationService test failed:', error.message);
|
||||
} finally {
|
||||
docService.close();
|
||||
}
|
||||
|
||||
// Test 3: MCP Server integration
|
||||
console.log('\n3️⃣ Testing MCP Server integration...');
|
||||
try {
|
||||
const { N8NMCPServer } = require('../dist/mcp/server');
|
||||
console.log(' ✅ MCP Server loads with enhanced documentation support');
|
||||
|
||||
// Check if new tools are available
|
||||
const { n8nTools } = require('../dist/mcp/tools');
|
||||
const enhancedTools = [
|
||||
'get_node_documentation',
|
||||
'search_node_documentation',
|
||||
'get_node_operations',
|
||||
'get_node_examples'
|
||||
];
|
||||
|
||||
const hasAllTools = enhancedTools.every(toolName =>
|
||||
n8nTools.some(tool => tool.name === toolName)
|
||||
);
|
||||
|
||||
if (hasAllTools) {
|
||||
console.log(' ✅ All enhanced documentation tools are available');
|
||||
enhancedTools.forEach(toolName => {
|
||||
const tool = n8nTools.find(t => t.name === toolName);
|
||||
console.log(` - ${toolName}: ${tool.description}`);
|
||||
});
|
||||
} else {
|
||||
console.log(' ⚠️ Some enhanced tools are missing');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(' ❌ MCP Server integration test failed:', error.message);
|
||||
}
|
||||
|
||||
console.log('\n✨ Enhanced documentation integration tests completed!');
|
||||
|
||||
// Cleanup
|
||||
await docFetcher.cleanup();
|
||||
}
|
||||
|
||||
// Run tests
|
||||
testEnhancedIntegration().catch(error => {
|
||||
console.error('Fatal error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
30
tests/test-package-info.js
Normal file
30
tests/test-package-info.js
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
|
||||
async function testPackageInfo() {
|
||||
console.log('🧪 Testing Package Info Extraction\n');
|
||||
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
const testNodes = [
|
||||
'n8n-nodes-base.Slack',
|
||||
'n8n-nodes-base.HttpRequest',
|
||||
'n8n-nodes-base.Function'
|
||||
];
|
||||
|
||||
for (const nodeType of testNodes) {
|
||||
console.log(`\n📦 Testing ${nodeType}:`);
|
||||
try {
|
||||
const result = await extractor.extractNodeSource(nodeType);
|
||||
console.log(` - Source Code: ${result.sourceCode ? '✅' : '❌'} (${result.sourceCode?.length || 0} bytes)`);
|
||||
console.log(` - Credential Code: ${result.credentialCode ? '✅' : '❌'} (${result.credentialCode?.length || 0} bytes)`);
|
||||
console.log(` - Package Name: ${result.packageInfo?.name || '❌ undefined'}`);
|
||||
console.log(` - Package Version: ${result.packageInfo?.version || '❌ undefined'}`);
|
||||
} catch (error) {
|
||||
console.log(` ❌ Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testPackageInfo().catch(console.error);
|
||||
82
tests/test-parsing-operations.js
Normal file
82
tests/test-parsing-operations.js
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const markdown = `
|
||||
## Operations
|
||||
|
||||
* **Channel**
|
||||
* **Archive** a channel.
|
||||
* **Close** a direct message or multi-person direct message.
|
||||
* **Create** a public or private channel-based conversation.
|
||||
* **Get** information about a channel.
|
||||
* **Get Many**: Get a list of channels in Slack.
|
||||
* **File**
|
||||
* **Get** a file.
|
||||
* **Get Many**: Get and filter team files.
|
||||
* **Upload**: Create or upload an existing file.
|
||||
|
||||
## Templates and examples
|
||||
`;
|
||||
|
||||
function extractOperations(markdown) {
|
||||
const operations = [];
|
||||
|
||||
// Find operations section
|
||||
const operationsMatch = markdown.match(/##\s+Operations\s*\n([\s\S]*?)(?=\n##|\n#|$)/i);
|
||||
if (!operationsMatch) {
|
||||
console.log('No operations section found');
|
||||
return operations;
|
||||
}
|
||||
|
||||
const operationsText = operationsMatch[1];
|
||||
console.log('Operations text:', operationsText.substring(0, 200));
|
||||
|
||||
// Parse operation structure
|
||||
let currentResource = null;
|
||||
const lines = operationsText.split('\n');
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
const trimmedLine = line.trim();
|
||||
|
||||
// Resource level (e.g., "* **Channel**")
|
||||
if (trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)) {
|
||||
currentResource = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*/)[1].trim();
|
||||
console.log(`Found resource: ${currentResource}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if we don't have a current resource
|
||||
if (!currentResource) continue;
|
||||
|
||||
// Operation level - look for indented bullets (4 spaces + *)
|
||||
if (line.match(/^\s{4}\*\s+/)) {
|
||||
console.log(`Found operation line: "${line}"`);
|
||||
|
||||
// Extract operation name and description
|
||||
const operationMatch = trimmedLine.match(/^\*\s+\*\*([^*]+)\*\*(.*)$/);
|
||||
if (operationMatch) {
|
||||
const operation = operationMatch[1].trim();
|
||||
let description = operationMatch[2].trim();
|
||||
|
||||
// Clean up description
|
||||
description = description.replace(/^:\s*/, '').replace(/\.$/, '').trim();
|
||||
|
||||
operations.push({
|
||||
resource: currentResource,
|
||||
operation,
|
||||
description: description || operation,
|
||||
});
|
||||
console.log(` Parsed: ${operation} - ${description}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return operations;
|
||||
}
|
||||
|
||||
const operations = extractOperations(markdown);
|
||||
console.log('\nTotal operations found:', operations.length);
|
||||
console.log('\nOperations:');
|
||||
operations.forEach(op => {
|
||||
console.log(`- ${op.resource}.${op.operation}: ${op.description}`);
|
||||
});
|
||||
5378
tests/test-results/extracted-nodes.json
Normal file
5378
tests/test-results/extracted-nodes.json
Normal file
File diff suppressed because one or more lines are too long
760
tests/test-results/test-summary.json
Normal file
760
tests/test-results/test-summary.json
Normal file
@@ -0,0 +1,760 @@
|
||||
{
|
||||
"totalTests": 6,
|
||||
"passed": 6,
|
||||
"failed": 0,
|
||||
"startTime": "2025-06-08T10:57:55.233Z",
|
||||
"endTime": "2025-06-08T10:57:59.249Z",
|
||||
"tests": [
|
||||
{
|
||||
"name": "Basic Node Extraction",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:55.236Z",
|
||||
"endTime": "2025-06-08T10:57:55.342Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"results": [
|
||||
{
|
||||
"nodeType": "@n8n/n8n-nodes-langchain.Agent",
|
||||
"extracted": false,
|
||||
"error": "Node source code not found for: @n8n/n8n-nodes-langchain.Agent"
|
||||
},
|
||||
{
|
||||
"nodeType": "n8n-nodes-base.Function",
|
||||
"extracted": true,
|
||||
"codeLength": 7449,
|
||||
"hasCredentials": false,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Function/Function.node.js"
|
||||
},
|
||||
{
|
||||
"nodeType": "n8n-nodes-base.Webhook",
|
||||
"extracted": true,
|
||||
"codeLength": 10667,
|
||||
"hasCredentials": false,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Webhook/Webhook.node.js"
|
||||
}
|
||||
],
|
||||
"successCount": 2,
|
||||
"totalTested": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "List Available Nodes",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:55.342Z",
|
||||
"endTime": "2025-06-08T10:57:55.689Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"totalNodes": 439,
|
||||
"packages": [
|
||||
"unknown"
|
||||
],
|
||||
"nodesByPackage": {
|
||||
"unknown": [
|
||||
"ActionNetwork",
|
||||
"ActiveCampaign",
|
||||
"ActiveCampaignTrigger",
|
||||
"AcuitySchedulingTrigger",
|
||||
"Adalo",
|
||||
"Affinity",
|
||||
"AffinityTrigger",
|
||||
"AgileCrm",
|
||||
"Airtable",
|
||||
"AirtableTrigger",
|
||||
"AirtableV1",
|
||||
"Amqp",
|
||||
"AmqpTrigger",
|
||||
"ApiTemplateIo",
|
||||
"Asana",
|
||||
"AsanaTrigger",
|
||||
"Automizy",
|
||||
"Autopilot",
|
||||
"AutopilotTrigger",
|
||||
"AwsLambda",
|
||||
"AwsSns",
|
||||
"AwsSnsTrigger",
|
||||
"AwsCertificateManager",
|
||||
"AwsComprehend",
|
||||
"AwsDynamoDB",
|
||||
"AwsElb",
|
||||
"AwsRekognition",
|
||||
"AwsS3",
|
||||
"AwsS3V1",
|
||||
"AwsS3V2",
|
||||
"AwsSes",
|
||||
"AwsSqs",
|
||||
"AwsTextract",
|
||||
"AwsTranscribe",
|
||||
"Bannerbear",
|
||||
"Baserow",
|
||||
"Beeminder",
|
||||
"BitbucketTrigger",
|
||||
"Bitly",
|
||||
"Bitwarden",
|
||||
"Box",
|
||||
"BoxTrigger",
|
||||
"Brandfetch",
|
||||
"Brevo",
|
||||
"BrevoTrigger",
|
||||
"Bubble",
|
||||
"CalTrigger",
|
||||
"CalendlyTrigger",
|
||||
"Chargebee",
|
||||
"ChargebeeTrigger",
|
||||
"CircleCi",
|
||||
"CiscoWebex",
|
||||
"CiscoWebexTrigger",
|
||||
"CitrixAdc",
|
||||
"Clearbit",
|
||||
"ClickUp",
|
||||
"ClickUpTrigger",
|
||||
"Clockify",
|
||||
"ClockifyTrigger",
|
||||
"Cloudflare",
|
||||
"Cockpit",
|
||||
"Coda",
|
||||
"Code",
|
||||
"CoinGecko",
|
||||
"CompareDatasets",
|
||||
"Compression",
|
||||
"Contentful",
|
||||
"ConvertKit",
|
||||
"ConvertKitTrigger",
|
||||
"Copper",
|
||||
"CopperTrigger",
|
||||
"Cortex",
|
||||
"CrateDb",
|
||||
"Cron",
|
||||
"CrowdDev",
|
||||
"CrowdDevTrigger",
|
||||
"Crypto",
|
||||
"CustomerIo",
|
||||
"CustomerIoTrigger",
|
||||
"DateTime",
|
||||
"DateTimeV1",
|
||||
"DateTimeV2",
|
||||
"DebugHelper",
|
||||
"DeepL",
|
||||
"Demio",
|
||||
"Dhl",
|
||||
"Discord",
|
||||
"Discourse",
|
||||
"Disqus",
|
||||
"Drift",
|
||||
"Dropbox",
|
||||
"Dropcontact",
|
||||
"E2eTest",
|
||||
"ERPNext",
|
||||
"EditImage",
|
||||
"Egoi",
|
||||
"ElasticSecurity",
|
||||
"Elasticsearch",
|
||||
"EmailReadImap",
|
||||
"EmailReadImapV1",
|
||||
"EmailReadImapV2",
|
||||
"EmailSend",
|
||||
"EmailSendV1",
|
||||
"EmailSendV2",
|
||||
"Emelia",
|
||||
"EmeliaTrigger",
|
||||
"ErrorTrigger",
|
||||
"EventbriteTrigger",
|
||||
"ExecuteCommand",
|
||||
"ExecuteWorkflow",
|
||||
"ExecuteWorkflowTrigger",
|
||||
"ExecutionData",
|
||||
"FacebookGraphApi",
|
||||
"FacebookTrigger",
|
||||
"FacebookLeadAdsTrigger",
|
||||
"FigmaTrigger",
|
||||
"FileMaker",
|
||||
"Filter",
|
||||
"Flow",
|
||||
"FlowTrigger",
|
||||
"FormTrigger",
|
||||
"FormIoTrigger",
|
||||
"FormstackTrigger",
|
||||
"Freshdesk",
|
||||
"Freshservice",
|
||||
"FreshworksCrm",
|
||||
"Ftp",
|
||||
"Function",
|
||||
"FunctionItem",
|
||||
"GetResponse",
|
||||
"GetResponseTrigger",
|
||||
"Ghost",
|
||||
"Git",
|
||||
"Github",
|
||||
"GithubTrigger",
|
||||
"Gitlab",
|
||||
"GitlabTrigger",
|
||||
"GoToWebinar",
|
||||
"GoogleAds",
|
||||
"GoogleAnalytics",
|
||||
"GoogleAnalyticsV1",
|
||||
"GoogleBigQuery",
|
||||
"GoogleBigQueryV1",
|
||||
"GoogleBooks",
|
||||
"GoogleCalendar",
|
||||
"GoogleCalendarTrigger",
|
||||
"GoogleChat",
|
||||
"GoogleCloudNaturalLanguage",
|
||||
"GoogleCloudStorage",
|
||||
"GoogleContacts",
|
||||
"GoogleDocs",
|
||||
"GoogleDrive",
|
||||
"GoogleDriveTrigger",
|
||||
"GoogleDriveV1",
|
||||
"GoogleFirebaseCloudFirestore",
|
||||
"GoogleFirebaseRealtimeDatabase",
|
||||
"GSuiteAdmin",
|
||||
"Gmail",
|
||||
"GmailTrigger",
|
||||
"GmailV1",
|
||||
"GmailV2",
|
||||
"GooglePerspective",
|
||||
"GoogleSheets",
|
||||
"GoogleSheetsTrigger",
|
||||
"GoogleSlides",
|
||||
"GoogleTasks",
|
||||
"GoogleTranslate",
|
||||
"YouTube",
|
||||
"Gotify",
|
||||
"Grafana",
|
||||
"GraphQL",
|
||||
"Grist",
|
||||
"GumroadTrigger",
|
||||
"HackerNews",
|
||||
"HaloPSA",
|
||||
"Harvest",
|
||||
"HelpScout",
|
||||
"HelpScoutTrigger",
|
||||
"HighLevel",
|
||||
"HomeAssistant",
|
||||
"Html",
|
||||
"HtmlExtract",
|
||||
"HttpRequest",
|
||||
"HttpRequestV1",
|
||||
"HttpRequestV2",
|
||||
"HttpRequestV3",
|
||||
"Hubspot",
|
||||
"HubspotTrigger",
|
||||
"HubspotV1",
|
||||
"HubspotV2",
|
||||
"HumanticAi",
|
||||
"Hunter",
|
||||
"ICalendar",
|
||||
"If",
|
||||
"Intercom",
|
||||
"Interval",
|
||||
"InvoiceNinja",
|
||||
"InvoiceNinjaTrigger",
|
||||
"ItemLists",
|
||||
"ItemListsV1",
|
||||
"ItemListsV2",
|
||||
"Iterable",
|
||||
"Jenkins",
|
||||
"Jira",
|
||||
"JiraTrigger",
|
||||
"JotFormTrigger",
|
||||
"Kafka",
|
||||
"KafkaTrigger",
|
||||
"Keap",
|
||||
"KeapTrigger",
|
||||
"Kitemaker",
|
||||
"KoBoToolbox",
|
||||
"KoBoToolboxTrigger",
|
||||
"Ldap",
|
||||
"Lemlist",
|
||||
"LemlistTrigger",
|
||||
"Line",
|
||||
"Linear",
|
||||
"LinearTrigger",
|
||||
"LingvaNex",
|
||||
"LinkedIn",
|
||||
"LocalFileTrigger",
|
||||
"LoneScale",
|
||||
"LoneScaleTrigger",
|
||||
"Mqtt",
|
||||
"MqttTrigger",
|
||||
"Magento2",
|
||||
"Mailcheck",
|
||||
"Mailchimp",
|
||||
"MailchimpTrigger",
|
||||
"MailerLite",
|
||||
"MailerLiteTrigger",
|
||||
"Mailgun",
|
||||
"Mailjet",
|
||||
"MailjetTrigger",
|
||||
"Mandrill",
|
||||
"ManualTrigger",
|
||||
"Markdown",
|
||||
"Marketstack",
|
||||
"Matrix",
|
||||
"Mattermost",
|
||||
"Mautic",
|
||||
"MauticTrigger",
|
||||
"Medium",
|
||||
"Merge",
|
||||
"MergeV1",
|
||||
"MergeV2",
|
||||
"MessageBird",
|
||||
"Metabase",
|
||||
"MicrosoftDynamicsCrm",
|
||||
"MicrosoftExcel",
|
||||
"MicrosoftExcelV1",
|
||||
"MicrosoftGraphSecurity",
|
||||
"MicrosoftOneDrive",
|
||||
"MicrosoftOutlook",
|
||||
"MicrosoftOutlookV1",
|
||||
"MicrosoftSql",
|
||||
"MicrosoftTeams",
|
||||
"MicrosoftToDo",
|
||||
"Mindee",
|
||||
"Misp",
|
||||
"Mocean",
|
||||
"MondayCom",
|
||||
"MongoDb",
|
||||
"MonicaCrm",
|
||||
"MoveBinaryData",
|
||||
"Msg91",
|
||||
"MySql",
|
||||
"MySqlV1",
|
||||
"N8n",
|
||||
"N8nTrainingCustomerDatastore",
|
||||
"N8nTrainingCustomerMessenger",
|
||||
"N8nTrigger",
|
||||
"Nasa",
|
||||
"Netlify",
|
||||
"NetlifyTrigger",
|
||||
"NextCloud",
|
||||
"NoOp",
|
||||
"NocoDB",
|
||||
"Notion",
|
||||
"NotionTrigger",
|
||||
"Npm",
|
||||
"Odoo",
|
||||
"OneSimpleApi",
|
||||
"Onfleet",
|
||||
"OnfleetTrigger",
|
||||
"OpenAi",
|
||||
"OpenThesaurus",
|
||||
"OpenWeatherMap",
|
||||
"Orbit",
|
||||
"Oura",
|
||||
"Paddle",
|
||||
"PagerDuty",
|
||||
"PayPal",
|
||||
"PayPalTrigger",
|
||||
"Peekalink",
|
||||
"Phantombuster",
|
||||
"PhilipsHue",
|
||||
"Pipedrive",
|
||||
"PipedriveTrigger",
|
||||
"Plivo",
|
||||
"PostBin",
|
||||
"PostHog",
|
||||
"Postgres",
|
||||
"PostgresTrigger",
|
||||
"PostgresV1",
|
||||
"PostmarkTrigger",
|
||||
"ProfitWell",
|
||||
"Pushbullet",
|
||||
"Pushcut",
|
||||
"PushcutTrigger",
|
||||
"Pushover",
|
||||
"QuestDb",
|
||||
"QuickBase",
|
||||
"QuickBooks",
|
||||
"QuickChart",
|
||||
"RabbitMQ",
|
||||
"RabbitMQTrigger",
|
||||
"Raindrop",
|
||||
"ReadBinaryFile",
|
||||
"ReadBinaryFiles",
|
||||
"ReadPDF",
|
||||
"Reddit",
|
||||
"Redis",
|
||||
"RedisTrigger",
|
||||
"RenameKeys",
|
||||
"RespondToWebhook",
|
||||
"Rocketchat",
|
||||
"RssFeedRead",
|
||||
"RssFeedReadTrigger",
|
||||
"Rundeck",
|
||||
"S3",
|
||||
"Salesforce",
|
||||
"Salesmate",
|
||||
"ScheduleTrigger",
|
||||
"SeaTable",
|
||||
"SeaTableTrigger",
|
||||
"SecurityScorecard",
|
||||
"Segment",
|
||||
"SendGrid",
|
||||
"Sendy",
|
||||
"SentryIo",
|
||||
"ServiceNow",
|
||||
"Set",
|
||||
"SetV1",
|
||||
"SetV2",
|
||||
"Shopify",
|
||||
"ShopifyTrigger",
|
||||
"Signl4",
|
||||
"Slack",
|
||||
"SlackV1",
|
||||
"SlackV2",
|
||||
"Sms77",
|
||||
"Snowflake",
|
||||
"SplitInBatches",
|
||||
"SplitInBatchesV1",
|
||||
"SplitInBatchesV2",
|
||||
"SplitInBatchesV3",
|
||||
"Splunk",
|
||||
"Spontit",
|
||||
"Spotify",
|
||||
"SpreadsheetFile",
|
||||
"SseTrigger",
|
||||
"Ssh",
|
||||
"Stackby",
|
||||
"Start",
|
||||
"StickyNote",
|
||||
"StopAndError",
|
||||
"Storyblok",
|
||||
"Strapi",
|
||||
"Strava",
|
||||
"StravaTrigger",
|
||||
"Stripe",
|
||||
"StripeTrigger",
|
||||
"Supabase",
|
||||
"SurveyMonkeyTrigger",
|
||||
"Switch",
|
||||
"SwitchV1",
|
||||
"SwitchV2",
|
||||
"SyncroMsp",
|
||||
"Taiga",
|
||||
"TaigaTrigger",
|
||||
"Tapfiliate",
|
||||
"Telegram",
|
||||
"TelegramTrigger",
|
||||
"TheHive",
|
||||
"TheHiveTrigger",
|
||||
"TheHiveProjectTrigger",
|
||||
"TimescaleDb",
|
||||
"Todoist",
|
||||
"TodoistV1",
|
||||
"TodoistV2",
|
||||
"TogglTrigger",
|
||||
"Totp",
|
||||
"TravisCi",
|
||||
"Trello",
|
||||
"TrelloTrigger",
|
||||
"Twake",
|
||||
"Twilio",
|
||||
"Twist",
|
||||
"Twitter",
|
||||
"TwitterV1",
|
||||
"TwitterV2",
|
||||
"TypeformTrigger",
|
||||
"UProc",
|
||||
"UnleashedSoftware",
|
||||
"Uplead",
|
||||
"UptimeRobot",
|
||||
"UrlScanIo",
|
||||
"VenafiTlsProtectDatacenter",
|
||||
"VenafiTlsProtectDatacenterTrigger",
|
||||
"VenafiTlsProtectCloud",
|
||||
"VenafiTlsProtectCloudTrigger",
|
||||
"Vero",
|
||||
"Vonage",
|
||||
"Wait",
|
||||
"Webflow",
|
||||
"WebflowTrigger",
|
||||
"Webhook",
|
||||
"Wekan",
|
||||
"WhatsApp",
|
||||
"Wise",
|
||||
"WiseTrigger",
|
||||
"WooCommerce",
|
||||
"WooCommerceTrigger",
|
||||
"Wordpress",
|
||||
"WorkableTrigger",
|
||||
"WorkflowTrigger",
|
||||
"WriteBinaryFile",
|
||||
"WufooTrigger",
|
||||
"Xero",
|
||||
"Xml",
|
||||
"Yourls",
|
||||
"Zammad",
|
||||
"Zendesk",
|
||||
"ZendeskTrigger",
|
||||
"ZohoCrm",
|
||||
"Zoom",
|
||||
"Zulip"
|
||||
]
|
||||
},
|
||||
"sampleNodes": [
|
||||
{
|
||||
"name": "ActionNetwork",
|
||||
"displayName": "Action Network",
|
||||
"description": "Consume the Action Network API",
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/ActionNetwork/ActionNetwork.node.js"
|
||||
},
|
||||
{
|
||||
"name": "ActiveCampaign",
|
||||
"displayName": "ActiveCampaign",
|
||||
"description": "Create and edit data in ActiveCampaign",
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/ActiveCampaign/ActiveCampaign.node.js"
|
||||
},
|
||||
{
|
||||
"name": "ActiveCampaignTrigger",
|
||||
"displayName": "ActiveCampaign Trigger",
|
||||
"description": "Handle ActiveCampaign events via webhooks",
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/ActiveCampaign/ActiveCampaignTrigger.node.js"
|
||||
},
|
||||
{
|
||||
"name": "AcuitySchedulingTrigger",
|
||||
"displayName": "Acuity Scheduling Trigger",
|
||||
"description": "Handle Acuity Scheduling events via webhooks",
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/AcuityScheduling/AcuitySchedulingTrigger.node.js"
|
||||
},
|
||||
{
|
||||
"name": "Adalo",
|
||||
"displayName": "Adalo",
|
||||
"description": "Consume Adalo API",
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Adalo/Adalo.node.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bulk Node Extraction",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:55.689Z",
|
||||
"endTime": "2025-06-08T10:57:58.574Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"totalAttempted": 10,
|
||||
"successCount": 6,
|
||||
"failureCount": 4,
|
||||
"timeElapsed": 2581,
|
||||
"results": [
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "ActionNetwork",
|
||||
"name": "ActionNetwork",
|
||||
"codeLength": 15810,
|
||||
"codeHash": "c0a880f5754b6b532ff787bdb253dc49ffd7f470f28aeddda5be0c73f9f9935f",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/ActionNetwork/ActionNetwork.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:56.009Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "ActiveCampaign",
|
||||
"name": "ActiveCampaign",
|
||||
"codeLength": 38399,
|
||||
"codeHash": "5ea90671718d20eecb6cddae2e21c91470fdb778e8be97106ee2539303422ad2",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/ActiveCampaign/ActiveCampaign.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:56.032Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"nodeType": "ActiveCampaignTrigger",
|
||||
"error": "Node source code not found for: ActiveCampaignTrigger"
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"nodeType": "AcuitySchedulingTrigger",
|
||||
"error": "Node source code not found for: AcuitySchedulingTrigger"
|
||||
},
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "Adalo",
|
||||
"name": "Adalo",
|
||||
"codeLength": 8234,
|
||||
"codeHash": "0fbcb0b60141307fdc3394154af1b2c3133fa6181aac336249c6c211fd24846f",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Adalo/Adalo.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:57.330Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "Affinity",
|
||||
"name": "Affinity",
|
||||
"codeLength": 16217,
|
||||
"codeHash": "e605ea187767403dfa55cd374690f7df563a0baa7ca6991d86d522dc101a2846",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Affinity/Affinity.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:57.343Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"nodeType": "AffinityTrigger",
|
||||
"error": "Node source code not found for: AffinityTrigger"
|
||||
},
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "AgileCrm",
|
||||
"name": "AgileCrm",
|
||||
"codeLength": 28115,
|
||||
"codeHash": "ce71c3b5dec23a48d24c5775e9bb79006ce395bed62b306c56340b5c772379c2",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/AgileCrm/AgileCrm.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:57.925Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"nodeType": "Airtable",
|
||||
"name": "Airtable",
|
||||
"codeLength": 936,
|
||||
"codeHash": "2d67e72931697178946f5127b43e954649c4c5e7ad9e29764796404ae96e7db5",
|
||||
"hasCredentials": true,
|
||||
"hasPackageInfo": true,
|
||||
"location": "node_modules/n8n-nodes-base/dist/nodes/Airtable/Airtable.node.js",
|
||||
"extractedAt": "2025-06-08T10:57:57.941Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"success": false,
|
||||
"nodeType": "AirtableTrigger",
|
||||
"error": "Node source code not found for: AirtableTrigger"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Database Schema Validation",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:58.574Z",
|
||||
"endTime": "2025-06-08T10:57:58.575Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"schemaValid": true,
|
||||
"tablesCount": 4,
|
||||
"estimatedStoragePerNode": 16834
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Error Handling",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:58.575Z",
|
||||
"endTime": "2025-06-08T10:57:59.244Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"totalTests": 3,
|
||||
"passed": 2,
|
||||
"results": [
|
||||
{
|
||||
"name": "Non-existent node",
|
||||
"nodeType": "non-existent-package.FakeNode",
|
||||
"expectedError": "not found",
|
||||
"passed": true,
|
||||
"actualError": "Node source code not found for: non-existent-package.FakeNode"
|
||||
},
|
||||
{
|
||||
"name": "Invalid node type format",
|
||||
"nodeType": "",
|
||||
"expectedError": "invalid",
|
||||
"passed": false,
|
||||
"actualError": "Node source code not found for: "
|
||||
},
|
||||
{
|
||||
"name": "Malformed package name",
|
||||
"nodeType": "@invalid@package.Node",
|
||||
"expectedError": "not found",
|
||||
"passed": true,
|
||||
"actualError": "Node source code not found for: @invalid@package.Node"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MCP Server Integration",
|
||||
"status": "passed",
|
||||
"startTime": "2025-06-08T10:57:59.244Z",
|
||||
"endTime": "2025-06-08T10:57:59.249Z",
|
||||
"error": null,
|
||||
"details": {
|
||||
"serverCreated": true,
|
||||
"config": {
|
||||
"port": 3000,
|
||||
"host": "0.0.0.0",
|
||||
"authToken": "test-token"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"extractedNodes": 6,
|
||||
"databaseSchema": {
|
||||
"tables": {
|
||||
"nodes": {
|
||||
"columns": {
|
||||
"id": "UUID PRIMARY KEY",
|
||||
"node_type": "VARCHAR(255) UNIQUE NOT NULL",
|
||||
"name": "VARCHAR(255) NOT NULL",
|
||||
"package_name": "VARCHAR(255)",
|
||||
"display_name": "VARCHAR(255)",
|
||||
"description": "TEXT",
|
||||
"version": "VARCHAR(50)",
|
||||
"code_hash": "VARCHAR(64) NOT NULL",
|
||||
"code_length": "INTEGER NOT NULL",
|
||||
"source_location": "TEXT",
|
||||
"extracted_at": "TIMESTAMP NOT NULL",
|
||||
"updated_at": "TIMESTAMP"
|
||||
},
|
||||
"indexes": [
|
||||
"node_type",
|
||||
"package_name",
|
||||
"code_hash"
|
||||
]
|
||||
},
|
||||
"node_source_code": {
|
||||
"columns": {
|
||||
"id": "UUID PRIMARY KEY",
|
||||
"node_id": "UUID REFERENCES nodes(id)",
|
||||
"source_code": "TEXT NOT NULL",
|
||||
"compiled_code": "TEXT",
|
||||
"source_map": "TEXT"
|
||||
}
|
||||
},
|
||||
"node_credentials": {
|
||||
"columns": {
|
||||
"id": "UUID PRIMARY KEY",
|
||||
"node_id": "UUID REFERENCES nodes(id)",
|
||||
"credential_type": "VARCHAR(255) NOT NULL",
|
||||
"credential_code": "TEXT NOT NULL",
|
||||
"required_fields": "JSONB"
|
||||
}
|
||||
},
|
||||
"node_metadata": {
|
||||
"columns": {
|
||||
"id": "UUID PRIMARY KEY",
|
||||
"node_id": "UUID REFERENCES nodes(id)",
|
||||
"package_info": "JSONB",
|
||||
"dependencies": "JSONB",
|
||||
"icon": "TEXT",
|
||||
"categories": "TEXT[]",
|
||||
"documentation_url": "TEXT"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
tests/test-slack-docs-issue.js
Executable file
133
tests/test-slack-docs-issue.js
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { DocumentationFetcher } = require('../dist/utils/documentation-fetcher');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
const { execSync } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
async function investigateSlackDocs() {
|
||||
console.log('=== Investigating Slack Node Documentation Issue ===\n');
|
||||
|
||||
const docsFetcher = new DocumentationFetcher();
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
try {
|
||||
// 1. Ensure docs repo is available
|
||||
console.log('1️⃣ Ensuring documentation repository...');
|
||||
await docsFetcher.ensureDocsRepository();
|
||||
|
||||
// 2. Check what files exist for Slack
|
||||
console.log('\n2️⃣ Searching for Slack documentation files...');
|
||||
const docsPath = path.join(process.cwd(), 'temp', 'n8n-docs');
|
||||
|
||||
try {
|
||||
const slackFiles = execSync(
|
||||
`find ${docsPath} -name "*slack*" -type f | grep -v ".git"`,
|
||||
{ encoding: 'utf-8' }
|
||||
).trim().split('\n').filter(Boolean);
|
||||
|
||||
console.log(`Found ${slackFiles.length} files with "slack" in the name:`);
|
||||
slackFiles.forEach(file => {
|
||||
const relPath = path.relative(docsPath, file);
|
||||
console.log(` - ${relPath}`);
|
||||
});
|
||||
|
||||
// Check content of each file
|
||||
console.log('\n3️⃣ Checking content of Slack-related files...');
|
||||
for (const file of slackFiles.slice(0, 5)) { // Check first 5 files
|
||||
if (file.endsWith('.md')) {
|
||||
const content = fs.readFileSync(file, 'utf-8');
|
||||
const firstLine = content.split('\n')[0];
|
||||
const isCredential = content.includes('credential') || content.includes('authentication');
|
||||
console.log(`\n 📄 ${path.basename(file)}`);
|
||||
console.log(` First line: ${firstLine}`);
|
||||
console.log(` Is credential doc: ${isCredential}`);
|
||||
|
||||
// Check if it mentions being a node or credential
|
||||
if (content.includes('# Slack node')) {
|
||||
console.log(' ✅ This is the Slack NODE documentation!');
|
||||
console.log(` Path: ${file}`);
|
||||
} else if (content.includes('# Slack credentials')) {
|
||||
console.log(' ⚠️ This is the Slack CREDENTIALS documentation');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Error searching for Slack files:', error.message);
|
||||
}
|
||||
|
||||
// 4. Test the getNodeDocumentation method
|
||||
console.log('\n4️⃣ Testing getNodeDocumentation for Slack...');
|
||||
const slackDocs = await docsFetcher.getNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDocs) {
|
||||
console.log(' ✅ Found documentation for Slack node');
|
||||
console.log(` URL: ${slackDocs.url}`);
|
||||
console.log(` Content preview: ${slackDocs.markdown.substring(0, 200)}...`);
|
||||
|
||||
// Check if it's credential or node docs
|
||||
const isCredentialDoc = slackDocs.markdown.includes('credential') ||
|
||||
slackDocs.markdown.includes('authentication') ||
|
||||
slackDocs.markdown.includes('# Slack credentials');
|
||||
const isNodeDoc = slackDocs.markdown.includes('# Slack node') ||
|
||||
slackDocs.markdown.includes('## Properties');
|
||||
|
||||
console.log(` Is credential doc: ${isCredentialDoc}`);
|
||||
console.log(` Is node doc: ${isNodeDoc}`);
|
||||
} else {
|
||||
console.log(' ❌ No documentation found for Slack node');
|
||||
}
|
||||
|
||||
// 5. Extract the Slack node source to understand its structure
|
||||
console.log('\n5️⃣ Extracting Slack node source code...');
|
||||
try {
|
||||
const slackNode = await extractor.extractNodeSource('n8n-nodes-base.slack');
|
||||
console.log(' ✅ Successfully extracted Slack node');
|
||||
console.log(` Location: ${slackNode.location}`);
|
||||
console.log(` Has credential code: ${!!slackNode.credentialCode}`);
|
||||
|
||||
// Parse the node definition
|
||||
const descMatch = slackNode.sourceCode.match(/description\s*[:=]\s*({[\s\S]*?})\s*[,;]/);
|
||||
if (descMatch) {
|
||||
console.log(' Found node description in source');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(' ❌ Failed to extract Slack node:', error.message);
|
||||
}
|
||||
|
||||
// 6. Check documentation structure
|
||||
console.log('\n6️⃣ Checking n8n-docs repository structure...');
|
||||
const docStructure = [
|
||||
'docs/integrations/builtin/app-nodes',
|
||||
'docs/integrations/builtin/core-nodes',
|
||||
'docs/integrations/builtin/trigger-nodes',
|
||||
'docs/integrations/builtin/credentials'
|
||||
];
|
||||
|
||||
for (const dir of docStructure) {
|
||||
const fullPath = path.join(docsPath, dir);
|
||||
try {
|
||||
const files = fs.readdirSync(fullPath);
|
||||
const slackFile = files.find(f => f.toLowerCase().includes('slack'));
|
||||
console.log(`\n 📁 ${dir}:`);
|
||||
if (slackFile) {
|
||||
console.log(` Found: ${slackFile}`);
|
||||
} else {
|
||||
console.log(` No Slack files found`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(` Directory doesn't exist`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Investigation failed:', error);
|
||||
} finally {
|
||||
// Cleanup
|
||||
await docsFetcher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// Run investigation
|
||||
investigateSlackDocs().catch(console.error);
|
||||
119
tests/test-slack-fix.js
Executable file
119
tests/test-slack-fix.js
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { NodeDocumentationService } = require('../dist/services/node-documentation-service');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
const { DocumentationFetcher } = require('../dist/utils/documentation-fetcher');
|
||||
|
||||
async function testSlackFix() {
|
||||
console.log('=== Testing Slack Node Fix ===\n');
|
||||
|
||||
const extractor = new NodeSourceExtractor();
|
||||
const docsFetcher = new DocumentationFetcher();
|
||||
|
||||
try {
|
||||
// Test 1: Node source extraction
|
||||
console.log('1️⃣ Testing Slack node source extraction...');
|
||||
const slackSource = await extractor.extractNodeSource('n8n-nodes-base.slack');
|
||||
console.log(` ✅ Source code found at: ${slackSource.location}`);
|
||||
console.log(` 📏 Source length: ${slackSource.sourceCode.length} bytes`);
|
||||
|
||||
// Extract display name from source
|
||||
const displayNameMatch = slackSource.sourceCode.match(/displayName\s*[:=]\s*['"`]([^'"`]+)['"`]/);
|
||||
console.log(` 📛 Display name: ${displayNameMatch ? displayNameMatch[1] : 'Not found'}`);
|
||||
|
||||
// Test 2: Documentation fetching
|
||||
console.log('\n2️⃣ Testing Slack documentation fetching...');
|
||||
const slackDocs = await docsFetcher.getNodeDocumentation('n8n-nodes-base.slack');
|
||||
|
||||
if (slackDocs) {
|
||||
console.log(` ✅ Documentation found`);
|
||||
console.log(` 📄 URL: ${slackDocs.url}`);
|
||||
|
||||
// Extract title from markdown
|
||||
const titleMatch = slackDocs.markdown.match(/title:\s*(.+)/);
|
||||
console.log(` 📝 Title: ${titleMatch ? titleMatch[1] : 'Not found'}`);
|
||||
|
||||
// Check if it's the correct documentation
|
||||
const isNodeDoc = slackDocs.markdown.includes('Slack node') ||
|
||||
slackDocs.markdown.includes('node documentation');
|
||||
const isCredentialDoc = slackDocs.markdown.includes('Slack credentials') &&
|
||||
!slackDocs.markdown.includes('node documentation');
|
||||
|
||||
console.log(` ✅ Is node documentation: ${isNodeDoc}`);
|
||||
console.log(` ❌ Is credential documentation: ${isCredentialDoc}`);
|
||||
|
||||
if (isNodeDoc && !isCredentialDoc) {
|
||||
console.log('\n🎉 SUCCESS: Slack node documentation is correctly fetched!');
|
||||
} else {
|
||||
console.log('\n⚠️ WARNING: Documentation may not be correct');
|
||||
}
|
||||
|
||||
// Show first few lines of content
|
||||
console.log('\n📋 Documentation preview:');
|
||||
const lines = slackDocs.markdown.split('\n').slice(0, 15);
|
||||
lines.forEach(line => console.log(` ${line}`));
|
||||
|
||||
} else {
|
||||
console.log(' ❌ No documentation found');
|
||||
}
|
||||
|
||||
// Test 3: Complete node info using NodeDocumentationService
|
||||
console.log('\n3️⃣ Testing complete node info storage...');
|
||||
const service = new NodeDocumentationService('./data/test-slack-fix.db');
|
||||
|
||||
try {
|
||||
// Parse node definition
|
||||
const nodeDefinition = {
|
||||
displayName: displayNameMatch ? displayNameMatch[1] : 'Slack',
|
||||
description: 'Send messages to Slack channels, users and conversations',
|
||||
category: 'Communication',
|
||||
icon: 'file:slack.svg',
|
||||
version: 2
|
||||
};
|
||||
|
||||
// Store node info
|
||||
await service.storeNode({
|
||||
nodeType: 'n8n-nodes-base.slack',
|
||||
name: 'slack',
|
||||
displayName: nodeDefinition.displayName,
|
||||
description: nodeDefinition.description,
|
||||
category: nodeDefinition.category,
|
||||
icon: nodeDefinition.icon,
|
||||
sourceCode: slackSource.sourceCode,
|
||||
credentialCode: slackSource.credentialCode,
|
||||
documentation: slackDocs?.markdown,
|
||||
documentationUrl: slackDocs?.url,
|
||||
packageName: 'n8n-nodes-base',
|
||||
version: nodeDefinition.version,
|
||||
hasCredentials: !!slackSource.credentialCode,
|
||||
isTrigger: false,
|
||||
isWebhook: false
|
||||
});
|
||||
|
||||
console.log(' ✅ Node info stored successfully');
|
||||
|
||||
// Retrieve and verify
|
||||
const retrievedNode = await service.getNodeInfo('n8n-nodes-base.slack');
|
||||
if (retrievedNode) {
|
||||
console.log(' ✅ Node retrieved successfully');
|
||||
console.log(` 📛 Display name: ${retrievedNode.displayName}`);
|
||||
console.log(` 📝 Has documentation: ${!!retrievedNode.documentation}`);
|
||||
console.log(` 📄 Documentation URL: ${retrievedNode.documentationUrl || 'N/A'}`);
|
||||
}
|
||||
|
||||
service.close();
|
||||
} catch (error) {
|
||||
console.error(' ❌ Error with node service:', error.message);
|
||||
service.close();
|
||||
}
|
||||
|
||||
console.log('\n✅ All tests completed!');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test failed:', error);
|
||||
} finally {
|
||||
await docsFetcher.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
testSlackFix().catch(console.error);
|
||||
137
tests/test-slack-node-complete.js
Normal file
137
tests/test-slack-node-complete.js
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { NodeDocumentationService } = require('../dist/services/node-documentation-service');
|
||||
const { EnhancedDocumentationFetcher } = require('../dist/utils/documentation-fetcher');
|
||||
const { NodeSourceExtractor } = require('../dist/utils/node-source-extractor');
|
||||
const path = require('path');
|
||||
|
||||
async function testSlackNode() {
|
||||
console.log('🧪 Testing Slack Node Complete Information Extraction\n');
|
||||
|
||||
const dbPath = path.join(__dirname, '../data/test-slack.db');
|
||||
const service = new NodeDocumentationService(dbPath);
|
||||
const fetcher = new EnhancedDocumentationFetcher();
|
||||
const extractor = new NodeSourceExtractor();
|
||||
|
||||
try {
|
||||
console.log('📚 Fetching Slack node documentation...');
|
||||
const docs = await fetcher.getEnhancedNodeDocumentation('n8n-nodes-base.Slack');
|
||||
|
||||
console.log('\n✅ Documentation Structure:');
|
||||
console.log(`- Title: ${docs.title}`);
|
||||
console.log(`- Has markdown: ${docs.markdown?.length > 0 ? 'Yes' : 'No'} (${docs.markdown?.length || 0} chars)`);
|
||||
console.log(`- Operations: ${docs.operations?.length || 0}`);
|
||||
console.log(`- API Methods: ${docs.apiMethods?.length || 0}`);
|
||||
console.log(`- Examples: ${docs.examples?.length || 0}`);
|
||||
console.log(`- Templates: ${docs.templates?.length || 0}`);
|
||||
console.log(`- Related Resources: ${docs.relatedResources?.length || 0}`);
|
||||
console.log(`- Required Scopes: ${docs.requiredScopes?.length || 0}`);
|
||||
|
||||
console.log('\n📋 Operations by Resource:');
|
||||
const resourceMap = new Map();
|
||||
if (docs.operations) {
|
||||
docs.operations.forEach(op => {
|
||||
if (!resourceMap.has(op.resource)) {
|
||||
resourceMap.set(op.resource, []);
|
||||
}
|
||||
resourceMap.get(op.resource).push(op);
|
||||
});
|
||||
}
|
||||
|
||||
for (const [resource, ops] of resourceMap) {
|
||||
console.log(`\n ${resource}:`);
|
||||
ops.forEach(op => {
|
||||
console.log(` - ${op.operation}: ${op.description}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n🔌 Sample API Methods:');
|
||||
if (docs.apiMethods) {
|
||||
docs.apiMethods.slice(0, 5).forEach(method => {
|
||||
console.log(` - ${method.operation} → ${method.apiMethod}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n💻 Extracting Slack node source code...');
|
||||
const sourceInfo = await extractor.extractNodeSource('n8n-nodes-base.Slack');
|
||||
|
||||
console.log('\n✅ Source Code Extraction:');
|
||||
console.log(`- Has source code: ${sourceInfo.sourceCode ? 'Yes' : 'No'} (${sourceInfo.sourceCode?.length || 0} chars)`);
|
||||
console.log(`- Has credential code: ${sourceInfo.credentialCode ? 'Yes' : 'No'} (${sourceInfo.credentialCode?.length || 0} chars)`);
|
||||
console.log(`- Package name: ${sourceInfo.packageInfo?.name}`);
|
||||
console.log(`- Package version: ${sourceInfo.packageInfo?.version}`);
|
||||
|
||||
// Store in database
|
||||
console.log('\n💾 Storing in database...');
|
||||
await service.storeNode({
|
||||
nodeType: 'n8n-nodes-base.Slack',
|
||||
name: 'Slack',
|
||||
displayName: 'Slack',
|
||||
description: 'Send and receive messages, manage channels, and more',
|
||||
category: 'Communication',
|
||||
documentationUrl: docs?.url || 'https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/',
|
||||
documentationMarkdown: docs?.markdown,
|
||||
documentationTitle: docs?.title,
|
||||
operations: docs?.operations,
|
||||
apiMethods: docs?.apiMethods,
|
||||
documentationExamples: docs?.examples,
|
||||
templates: docs?.templates,
|
||||
relatedResources: docs?.relatedResources,
|
||||
requiredScopes: docs?.requiredScopes,
|
||||
sourceCode: sourceInfo.sourceCode || '',
|
||||
credentialCode: sourceInfo.credentialCode,
|
||||
packageName: sourceInfo.packageInfo?.name || 'n8n-nodes-base',
|
||||
version: sourceInfo.packageInfo?.version,
|
||||
hasCredentials: true,
|
||||
isTrigger: false,
|
||||
isWebhook: false
|
||||
});
|
||||
|
||||
// Retrieve and verify
|
||||
console.log('\n🔍 Retrieving from database...');
|
||||
const storedNode = await service.getNodeInfo('n8n-nodes-base.Slack');
|
||||
|
||||
console.log('\n✅ Verification Results:');
|
||||
console.log(`- Node found: ${storedNode ? 'Yes' : 'No'}`);
|
||||
if (storedNode) {
|
||||
console.log(`- Has operations: ${storedNode.operations?.length > 0 ? 'Yes' : 'No'} (${storedNode.operations?.length || 0})`);
|
||||
console.log(`- Has API methods: ${storedNode.apiMethods?.length > 0 ? 'Yes' : 'No'} (${storedNode.apiMethods?.length || 0})`);
|
||||
console.log(`- Has examples: ${storedNode.documentationExamples?.length > 0 ? 'Yes' : 'No'} (${storedNode.documentationExamples?.length || 0})`);
|
||||
console.log(`- Has source code: ${storedNode.sourceCode ? 'Yes' : 'No'}`);
|
||||
console.log(`- Has credential code: ${storedNode.credentialCode ? 'Yes' : 'No'}`);
|
||||
}
|
||||
|
||||
// Test search
|
||||
console.log('\n🔍 Testing search...');
|
||||
const searchResults = await service.searchNodes('message send');
|
||||
const slackInResults = searchResults.some(r => r.nodeType === 'n8n-nodes-base.Slack');
|
||||
console.log(`- Slack found in search results: ${slackInResults ? 'Yes' : 'No'}`);
|
||||
|
||||
console.log('\n✅ Complete Information Test Summary:');
|
||||
const hasCompleteInfo =
|
||||
storedNode &&
|
||||
storedNode.operations?.length > 0 &&
|
||||
storedNode.apiMethods?.length > 0 &&
|
||||
storedNode.sourceCode &&
|
||||
storedNode.documentationMarkdown;
|
||||
|
||||
console.log(`- Has complete information: ${hasCompleteInfo ? '✅ YES' : '❌ NO'}`);
|
||||
|
||||
if (!hasCompleteInfo) {
|
||||
console.log('\n❌ Missing Information:');
|
||||
if (!storedNode) console.log(' - Node not stored properly');
|
||||
if (!storedNode?.operations?.length) console.log(' - No operations extracted');
|
||||
if (!storedNode?.apiMethods?.length) console.log(' - No API methods extracted');
|
||||
if (!storedNode?.sourceCode) console.log(' - No source code extracted');
|
||||
if (!storedNode?.documentationMarkdown) console.log(' - No documentation extracted');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Test failed:', error);
|
||||
} finally {
|
||||
await service.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testSlackNode().catch(console.error);
|
||||
Reference in New Issue
Block a user