adpa-enterprise-framework-automation
Version:
Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe
297 lines • 10.4 kB
JavaScript
/**
* Enhanced Template Repository
*
* Provides unified access to both static (legacy) and database-backed templates
* with full CRUD operations, search, and PMBOK compliance validation.
*/
export class TemplateRepository {
db;
constructor(db) {
this.db = db;
}
/**
* Get all system templates (migrated from static GENERATION_TASKS)
*/
async getSystemTemplates() {
const sql = `
SELECT * FROM templates
WHERE is_system = true AND is_active = true
ORDER BY category, name
`;
const result = await this.db.query(sql);
return result.rows || result;
}
/**
* Get user-created templates
*/
async getUserTemplates(userId) {
const sql = `
SELECT * FROM templates
WHERE is_system = false AND is_active = true
${userId ? 'AND created_by = $1' : ''}
ORDER BY created_at DESC
`;
const params = userId ? [userId] : [];
const result = await this.db.query(sql, params);
return result.rows || result;
}
/**
* Get all active templates (system + user)
*/
async getAllTemplates() {
const sql = `
SELECT * FROM templates
WHERE is_active = true
ORDER BY
is_system DESC, -- System templates first
category,
name
`;
const result = await this.db.query(sql);
return result.rows || result;
}
/**
* Search templates with advanced filtering
*/
async searchTemplates(query) {
let sql = `
SELECT t.*, tc.description as category_description
FROM templates t
LEFT JOIN template_categories tc ON t.category = tc.name
WHERE t.is_active = true
`;
const params = [];
let paramIndex = 1;
if (query.category) {
sql += ` AND t.category = $${paramIndex}`;
params.push(query.category);
paramIndex++;
}
if (query.template_type) {
sql += ` AND t.template_type = $${paramIndex}`;
params.push(query.template_type);
paramIndex++;
}
if (query.is_system !== undefined) {
sql += ` AND t.is_system = $${paramIndex}`;
params.push(query.is_system);
paramIndex++;
}
if (query.search_text) {
sql += ` AND (
t.name ILIKE $${paramIndex} OR
t.description ILIKE $${paramIndex} OR
t.ai_instructions ILIKE $${paramIndex}
)`;
params.push(`%${query.search_text}%`);
paramIndex++;
}
sql += ` ORDER BY t.is_system DESC, t.name`;
if (query.limit) {
sql += ` LIMIT $${paramIndex}`;
params.push(query.limit);
paramIndex++;
}
if (query.offset) {
sql += ` OFFSET $${paramIndex}`;
params.push(query.offset);
}
const result = await this.db.query(sql, params);
return result.rows || result;
}
/**
* Get template by ID
*/
async getTemplateById(id) {
const sql = `
SELECT t.*, tc.description as category_description
FROM templates t
LEFT JOIN template_categories tc ON t.category = tc.name
WHERE t.id = $1
`;
const result = await this.db.query(sql, [id]);
return (result.rows?.[0] || result[0]) || null;
}
/**
* Get template by generation function (for backward compatibility)
*/
async getTemplateByFunction(functionName) {
const sql = `
SELECT * FROM templates
WHERE generation_function = $1 AND is_active = true
LIMIT 1
`;
const result = await this.db.query(sql, [functionName]);
return (result.rows?.[0] || result[0]) || null;
}
/**
* Create new template
*/
async createTemplate(template) {
const sql = `
INSERT INTO templates (
name, description, category, template_type,
ai_instructions, prompt_template, generation_function,
metadata, version, is_active, is_system, created_by
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12
) RETURNING *
`;
const params = [
template.name,
template.description,
template.category,
template.template_type,
template.ai_instructions,
template.prompt_template,
template.generation_function,
JSON.stringify(template.metadata),
template.version,
template.is_active,
template.is_system,
template.created_by
];
const result = await this.db.query(sql, params);
return result.rows?.[0] || result[0];
}
/**
* Update existing template (creates new version)
*/
async updateTemplate(id, updates, updatedBy) {
return this.db.transaction(async (trx) => {
// Get current template
const currentTemplate = await this.getTemplateById(id);
if (!currentTemplate) {
throw new Error(`Template ${id} not found`);
}
// Create version record
await trx.query(`
INSERT INTO template_versions (template_id, version, changes, created_by)
VALUES ($1, $2, $3, $4)
`, [
id,
currentTemplate.version,
JSON.stringify(updates),
updatedBy
]);
// Update template
const setClause = Object.keys(updates)
.map((key, index) => `${key} = $${index + 2}`)
.join(', ');
const sql = `
UPDATE templates
SET ${setClause}, version = version + 1, updated_at = NOW()
WHERE id = $1
RETURNING *
`;
const params = [id, ...Object.values(updates)];
const result = await trx.query(sql, params);
return result.rows?.[0] || result[0];
});
}
/**
* Soft delete template (mark as inactive)
*/
async deleteTemplate(id) {
const sql = `
UPDATE templates
SET is_active = false, updated_at = NOW()
WHERE id = $1
`;
await this.db.query(sql, [id]);
}
/**
* Get template categories
*/
async getCategories() {
const sql = `
SELECT tc.*, COUNT(t.id) as template_count
FROM template_categories tc
LEFT JOIN templates t ON tc.name = t.category AND t.is_active = true
GROUP BY tc.id, tc.name, tc.description, tc.sort_order
ORDER BY tc.sort_order, tc.name
`;
const result = await this.db.query(sql);
return result.rows || result;
}
/**
* Convert database template to legacy GenerationTask format (for backward compatibility)
*/
templateToGenerationTask(template) {
const metadata = template.metadata || {};
return {
key: metadata.key || template.generation_function || template.name.toLowerCase().replace(/\s+/g, '-'),
name: template.name,
func: template.generation_function || 'getAiGenericDocument',
category: template.category,
emoji: metadata.emoji || '📄',
priority: metadata.priority || 100,
dependencies: metadata.dependencies || [],
pmbokRef: metadata.pmbokRef
};
}
/**
* Get all templates as legacy GenerationTask format
*/
async getTemplatesAsGenerationTasks() {
const templates = await this.getAllTemplates();
return templates.map(template => this.templateToGenerationTask(template));
}
/**
* Validate template for PMBOK compliance
*/
validatePMBOKCompliance(template) {
const issues = [];
// Check required fields
if (!template.name || template.name.length < 3) {
issues.push('Template name must be at least 3 characters');
}
if (!template.description || template.description.length < 10) {
issues.push('Template description must be at least 10 characters');
}
if (!template.ai_instructions || template.ai_instructions.length < 20) {
issues.push('AI instructions must be at least 20 characters');
}
if (!template.category) {
issues.push('Template must have a category');
}
// Check PMBOK-specific requirements
if (template.template_type === 'pmbok') {
if (!template.ai_instructions.toLowerCase().includes('pmbok')) {
issues.push('PMBOK templates should reference PMBOK standards in AI instructions');
}
if (!template.prompt_template.toLowerCase().includes('professional')) {
issues.push('PMBOK templates should specify professional language requirements');
}
}
// Check metadata structure
if (template.metadata) {
const metadata = template.metadata;
if (!metadata.emoji) {
issues.push('Template should have an emoji for display purposes');
}
}
return {
valid: issues.length === 0,
issues
};
}
/**
* Get template usage statistics
*/
async getTemplateStats() {
const sql = `
SELECT
COUNT(*) as total_templates,
COUNT(CASE WHEN is_system THEN 1 END) as system_templates,
COUNT(CASE WHEN NOT is_system THEN 1 END) as user_templates,
COUNT(CASE WHEN is_active THEN 1 END) as active_templates,
COUNT(DISTINCT category) as categories,
COUNT(CASE WHEN template_type = 'pmbok' THEN 1 END) as pmbok_templates
FROM templates
`;
const result = await this.db.query(sql);
return result.rows?.[0] || result[0];
}
}
//# sourceMappingURL=TemplateRepository.js.map