hana-cli
Version:
HANA Developer Command Line Interface
569 lines (538 loc) • 18.4 kB
text/typescript
/**
* Documentation Knowledge Base
*
* Aggregates and indexes documentation from the project's docs/ folder
* to provide context-aware guidance and parameter information for the MCP server.
*/
import { readFileSync } from 'fs';
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
function readDocsFile(relativePath: string, fallbackText: string): string {
try {
const filePath = join(__dirname, '..', '..', relativePath);
return readFileSync(filePath, 'utf-8');
} catch {
return fallbackText;
}
}
interface ParameterInfo {
name: string;
alias?: string | string[];
type: string;
default: string;
description: string;
}
interface CommandCategory {
name: string;
description: string;
examples?: string[];
standardParameters?: ParameterInfo[];
}
interface SecurityInfo {
topic: string;
description: string;
details: string[];
}
interface ConnectionStep {
order: number;
name: string;
description: string;
file: string;
notes?: string;
}
interface ProjectResource {
path: string;
title: string;
description: string;
contents: string;
}
export class ReadmeKnowledgeBase {
/**
* Global Standard Parameters - Available in all commands
*/
static readonly GLOBAL_PARAMETERS: ParameterInfo[] = [
{
name: '--admin',
alias: '-a',
type: 'boolean',
default: 'false',
description: 'Connect via admin credentials (uses default-env-admin.json)'
},
{
name: '--conn',
type: 'string',
default: 'not set',
description: 'Connection filename to override default-env.json'
},
{
name: '--disableVerbose',
alias: '--quiet',
type: 'boolean',
default: 'false',
description: 'Disable verbose output for scripting'
},
{
name: '--debug',
alias: '-d',
type: 'boolean',
default: 'false',
description: 'Enable debug output for troubleshooting'
},
];
/**
* Standardized command categories with their parameter conventions
*/
static readonly COMMAND_CATEGORIES: Record<string, CommandCategory> = {
'data-manipulation': {
name: 'Data Manipulation',
description: 'Commands for importing, exporting, and transforming data',
standardParameters: [
{
name: '--schema',
alias: '-s',
type: 'string',
default: '**CURRENT_SCHEMA**',
description: 'Target schema name'
},
{
name: '--sourceSchema',
alias: '-ss',
type: 'string',
default: '**CURRENT_SCHEMA**',
description: 'Source schema for operations'
},
{
name: '--targetSchema',
alias: '-ts',
type: 'string',
default: '**CURRENT_SCHEMA**',
description: 'Target schema for operations'
},
{
name: '--table',
alias: '-t',
type: 'string',
default: 'not set',
description: 'Table name(s)'
},
{
name: '--sourceTable',
alias: '-st',
type: 'string',
default: 'not set',
description: 'Source table name'
},
{
name: '--targetTable',
alias: '-tt',
type: 'string',
default: 'not set',
description: 'Target table name'
},
{
name: '--output',
alias: '-o',
type: 'string',
default: 'not set',
description: 'Output file path'
},
{
name: '--format',
alias: '-f',
type: 'string',
default: 'csv/json/summary',
description: 'Output format'
},
{
name: '--limit',
alias: '-l',
type: 'number',
default: '1000',
description: 'Maximum result set size'
},
{
name: '--batchSize',
alias: '-b, --batch',
type: 'number',
default: '1000',
description: 'Batch size for processing'
},
{
name: '--timeout',
alias: '-to',
type: 'number',
default: '3600',
description: 'Operation timeout in seconds (1 hour)'
},
{
name: '--dryRun',
alias: '-dr, --preview',
type: 'boolean',
default: 'false',
description: 'Preview operation without committing changes'
},
{
name: '--profile',
alias: '-p',
type: 'string',
default: 'not set',
description: 'Database profile (hana, postgresql, sqlite, etc.)'
},
]
},
'batch-operations': {
name: 'Batch Operations',
description: 'Commands for performing actions on multiple objects (massGrant, massUpdate, massDelete, etc.)',
standardParameters: [
{
name: '--schema',
alias: '-s',
type: 'string',
default: 'not set',
description: 'Schema containing objects'
},
{
name: '--object',
alias: '-o',
type: 'string',
default: 'not set',
description: 'Object name pattern'
},
{
name: '--limit',
alias: '-l',
type: 'number',
default: '1000',
description: 'Maximum objects to process'
},
{
name: '--dryRun',
alias: '-dr, --preview',
type: 'boolean',
default: 'false',
description: 'Preview without executing'
},
{
name: '--log',
type: 'boolean',
default: 'false',
description: 'Enable operation logging'
},
]
},
'list-inspect': {
name: 'List/Inspect',
description: 'Commands for listing and inspecting database objects (tables, views, schemas, etc.)',
standardParameters: [
{
name: '--schema',
alias: '-s',
type: 'string',
default: '**CURRENT_SCHEMA**',
description: 'Filter by schema'
},
{
name: '--limit',
alias: '-l',
type: 'number',
default: '200',
description: 'Maximum results to return'
},
{
name: '--profile',
alias: '-p',
type: 'string',
default: 'not set',
description: 'Database profile selector'
},
]
},
};
/**
* Connection resolution order (7-step process)
*/
static readonly CONNECTION_RESOLUTION: ConnectionStep[] = [
{
order: 1,
name: 'Admin Credentials Override',
file: 'default-env-admin.json',
description: 'Highest priority. If --admin flag is set, overrides all other parameters.',
notes: 'Search in current directory and 5 parent directories'
},
{
order: 2,
name: 'CAP/CDS Secure Binding',
file: '.cdsrc-private.json',
description: 'Most secure option. Uses cds bind functionality to lookup credentials dynamically.',
notes: 'Adds a few seconds per command but credentials are not stored locally'
},
{
order: 3,
name: 'Environment Variables',
file: '.env',
description: 'Environment file with VCAP_SERVICES section',
notes: 'Search up to 5 parent directories'
},
{
order: 4,
name: 'Connection Parameter',
file: '--conn parameter',
description: 'Explicit connection file specified via --conn parameter',
notes: 'Search in current directory, parent directories, or ${homedir}/.hana-cli/'
},
{
order: 5,
name: 'Home Directory',
file: '${homedir}/.hana-cli/configured file',
description: 'Connection file in user home directory .hana-cli folder',
notes: 'Used after --conn parameter file lookup fails'
},
{
order: 6,
name: 'Default Configuration',
file: 'default-env.json',
description: 'Standard default configuration file',
notes: 'Search in current directory and 5 parent directories'
},
{
order: 7,
name: 'Home Default',
file: '${homedir}/.hana-cli/default.json',
description: 'Last resort fallback in user home directory',
notes: 'Final fallback if all other methods fail'
},
];
/**
* Security best practices and connection guidelines
*/
static readonly SECURITY_GUIDELINES: SecurityInfo[] = [
{
topic: 'Connection Configuration',
description: 'Best practices for managing database connections safely',
details: [
'Never commit credentials to version control (use .gitignore)',
'Use .cdsrc-private.json with cds bind for cloud environments - most secure as credentials are not stored locally',
'Use default-env-admin.json for local admin connections only',
'Keep .env files out of repositories',
'Use environment variables for sensitive connection strings in CI/CD pipelines',
'The tool searches in parent directories to allow flexible project structures',
]
},
{
topic: 'SQL Injection Protection',
description: 'Built-in protection against SQL injection attacks',
details: [
'All schema, table, and user parameters are validated against SQL injection patterns',
'The sqlInjection utility is used across all commands to sanitize inputs',
'Wildcard patterns (%) are allowed only in specific contexts (e.g., schema/table filtering)',
'Comments (--) are automatically rejected in parameter values',
'Always use parameterized queries when possible',
]
},
{
topic: 'Parameter Security',
description: 'Secure handling of parameters in commands',
details: [
'Use --disableVerbose/--quiet flags when running in scripts to prevent credential logging',
'Avoid passing passwords directly in command line; use connection files instead',
'The --debug flag shows detailed information but should be disabled in production',
'Use --dryRun/--preview to test operations before execution',
'Always review operation logs in sensitive environments',
]
},
{
topic: 'Environment Security',
description: 'Securing the execution environment',
details: [
'Run hana-cli in isolated environments for multi-tenant scenarios',
'Use process isolation when executing from enterprise applications',
'Implement proper access controls for connection configuration files',
'Monitor command execution logs in regulated environments',
'Use the --timeout parameter to prevent long-running operations',
]
}
];
/**
* Parameter naming conventions and best practices
*/
static readonly NAMING_CONVENTIONS = {
singleOperations: 'Use singular names (e.g., schema, table, user)',
sourceTarget: 'Use paired names (e.g., sourceSchema/targetSchema)',
booleanFlags: 'Use descriptive names (e.g., dryRun, includeHeaders)',
aggregation: 'Use plural or descriptive names (e.g., columns, indexes)',
aliases: {
singleLetter: 'First letter of parameter (e.g., -s for schema)',
extended: 'Meaningful abbreviation (e.g., -dr for dryRun)',
noSelfReference: 'Parameter name itself is not used as alias',
maxAliases: 'Parameters have at most 2 aliases'
}
};
/**
* Key project folders and their purposes
*/
static readonly PROJECT_STRUCTURE = {
'bin': 'CLI command definitions and entry points (150+ commands)',
'app': 'Web UI (Fiori Launchpad) with UI5 applications',
'routes': 'HTTP/REST API endpoints (27+ endpoints for programmatic access)',
'utils': 'Reusable utility modules for connections, security, database abstraction',
'docs': 'In-depth command documentation and examples',
'tests': 'Test suites for CLI commands and functionality',
'types': 'TypeScript type definitions',
'mcp-server': 'Model Context Protocol server for AI assistants',
'_i18n': 'Internationalization files (multiple languages)'
};
/**
* Key markdown documentation files and their contents
*/
static readonly DOCUMENTATION_RESOURCES: Record<string, ProjectResource> = {
'docs-home': {
path: 'docs/README.md',
title: 'Documentation Home',
description: 'Entry point for hana-cli documentation, guides, and references',
contents: 'Covers: Documentation overview, navigation, and key entry points'
},
'getting-started': {
path: 'docs/01-getting-started/index.md',
title: 'Getting Started',
description: 'Installation, configuration, and quick start guidance',
contents: 'Covers: Requirements, setup, configuration, and first commands'
},
'web-ui': {
path: 'docs/03-features/web-ui/index.md',
title: 'Web UI (Fiori) Guide',
description: 'Documentation for the Web UI and UI5 applications',
contents: 'Covers: Web UI architecture, feature overview, and usage'
},
'api-reference': {
path: 'docs/04-api-reference/index.md',
title: 'API Reference',
description: 'HTTP API reference and integration guidance',
contents: 'Covers: API overview, endpoints, and integration patterns'
},
'http-routes': {
path: 'docs/04-api-reference/http-routes.md',
title: 'HTTP Routes',
description: 'Detailed documentation for REST endpoints',
contents: 'Covers: REST endpoints, request/response formats, and usage'
},
'mcp-server': {
path: 'docs/05-development/mcp-server/index.md',
title: 'MCP Server Documentation',
description: 'Development and configuration guide for MCP integration',
contents: 'Covers: MCP server architecture, setup, tools, prompts, and resources'
},
'swagger': {
path: 'docs/04-api-reference/swagger-implementation.md',
title: 'Swagger/OpenAPI Implementation',
description: 'Swagger/OpenAPI tooling and implementation details',
contents: 'Covers: Swagger setup, OpenAPI support, and interactive testing'
},
'command-reference': {
path: 'docs/99-reference/command-reference.md',
title: 'Command Reference',
description: 'Reference index for CLI commands and categories',
contents: 'Covers: Command taxonomy, category mapping, and references'
},
};
/**
* Get connection resolution guide with detailed explanations
*/
static getConnectionGuide(): string {
return readDocsFile(
'docs/01-getting-started/configuration.md',
'Connection guide documentation is not available. Check docs/01-getting-started/configuration.md.'
);
}
/**
* Get standard parameters for a specific command category
*/
static getStandardParameters(category: string): ParameterInfo[] {
const categoryData = this.COMMAND_CATEGORIES[category];
if (!categoryData) {
return [];
}
// Combine global and category-specific parameters
return [...this.GLOBAL_PARAMETERS, ...(categoryData.standardParameters || [])];
}
/**
* Get security guidelines as formatted text
*/
static getSecurityGuidelines(): string {
return readDocsFile(
'docs/03-features/knowledge-base.md',
'Security guidance is not available. Check docs/03-features/knowledge-base.md.'
);
}
/**
* Get parameter guidelines for a specific command category
*/
static getParameterGuide(category: string): string {
const docText = readDocsFile(
'docs/03-features/cli-features.md',
'CLI features documentation is not available. Check docs/03-features/cli-features.md.'
);
return `
}
/**
* Get project structure overview
*/
static getProjectStructure(): string {
return readDocsFile(
'docs/05-development/index.md',
'Project structure documentation is not available. Check docs/05-development/index.md.'
);
}
/**
* Get best practices and naming conventions guide
*/
static getBestPractices(): string {
return readDocsFile(
'docs/03-features/knowledge-base.md',
'Best practices guidance is not available. Check docs/03-features/knowledge-base.md.'
);
}
/**
* Search documentation by keyword
*/
static searchDocumentation(query: string): string {
const lowerQuery = query.toLowerCase();
const results: string[] = [];
// Search in command categories
Object.entries(this.COMMAND_CATEGORIES).forEach(([key, category]) => {
if (key.includes(lowerQuery) || category.description.toLowerCase().includes(lowerQuery)) {
results.push(`**Command Category**: ${category.name} - ${category.description}`);
}
});
// Search in documentation resources
Object.values(this.DOCUMENTATION_RESOURCES).forEach(resource => {
if (resource.title.toLowerCase().includes(lowerQuery) ||
resource.description.toLowerCase().includes(lowerQuery) ||
resource.contents.toLowerCase().includes(lowerQuery)) {
results.push(`**Documentation**: [${resource.title}](${resource.path}) - ${resource.description}`);
}
});
// Search in project structure
Object.entries(this.PROJECT_STRUCTURE).forEach(([folder, purpose]) => {
if (purpose.toLowerCase().includes(lowerQuery)) {
results.push(`**Project Folder**: \`${folder}/\` - ${purpose}`);
}
});
// Search in security guidelines
this.SECURITY_GUIDELINES.forEach(guide => {
if (guide.topic.toLowerCase().includes(lowerQuery) ||
guide.description.toLowerCase().includes(lowerQuery)) {
results.push(`**Security Guideline**: ${guide.topic} - ${guide.description}`);
}
});
if (results.length === 0) {
return `No documentation found matching "${query}". Try searching for:
- Command categories (data-manipulation, batch-operations, list-inspect)
- Topics (security, connection, parameters, naming, profile)
- Folders (bin, app, routes, utils, docs)`;
}
return `
}
}
export default ReadmeKnowledgeBase;