230 lines
6.1 KiB
JavaScript
230 lines
6.1 KiB
JavaScript
/**
|
|
* Workflow Service
|
|
* Handles workflow fetching with lazy loading and caching
|
|
* Workflows are only loaded when first requested (not at startup)
|
|
*/
|
|
|
|
const apiService = require('./api-service').getInstance();
|
|
const profileManager = require('../config/profile-manager');
|
|
|
|
// Cache state
|
|
let workflowCache = null;
|
|
let cacheTimestamp = null;
|
|
const CACHE_TTL = parseInt(process.env.WORKFLOW_CACHE_TTL) || 3600000; // 1 hour in milliseconds
|
|
|
|
// Clear cache when profile changes — workflows are per-tenant, so the previous
|
|
// profile's cache is meaningless after a switch.
|
|
profileManager.onSwitch(() => clearCache());
|
|
|
|
/**
|
|
* Check if cache is still valid
|
|
*/
|
|
function isCacheValid() {
|
|
if (!workflowCache || !cacheTimestamp) {
|
|
return false;
|
|
}
|
|
|
|
const now = Date.now();
|
|
const age = now - cacheTimestamp;
|
|
return age < CACHE_TTL;
|
|
}
|
|
|
|
/**
|
|
* Fetch all workflows from API with pagination
|
|
* Uses high page size (5000) to minimize API calls
|
|
*/
|
|
async function fetchAllWorkflows() {
|
|
// Check cache validity
|
|
if (isCacheValid()) {
|
|
console.error('[Workflow] Using cached data');
|
|
return workflowCache;
|
|
}
|
|
|
|
console.error('[Workflow] Cache expired or empty, fetching from API...');
|
|
|
|
try {
|
|
let allWorkflows = [];
|
|
let offset = 0;
|
|
const pageSize = parseInt(process.env.WORKFLOW_PAGE_SIZE) || 5000;
|
|
const profile = profileManager.getCurrent();
|
|
const application = profile.application;
|
|
const tenant = profile.tenant;
|
|
|
|
while (true) {
|
|
const body = [application, tenant, pageSize, offset];
|
|
|
|
console.error(`[Workflow] Fetching page: offset=${offset}, pageSize=${pageSize}`);
|
|
|
|
// Use AD API (useAdApi=true)
|
|
const response = await apiService.post('/Workflow/GetByApplication', body, true);
|
|
|
|
// Extract entities array from response
|
|
const workflows = response?.entities || [];
|
|
|
|
// Check if response is valid
|
|
if (!workflows || workflows.length === 0) {
|
|
console.error('[Workflow] No more workflows to fetch');
|
|
break;
|
|
}
|
|
|
|
allWorkflows = allWorkflows.concat(workflows);
|
|
console.error(`[Workflow] Fetched ${workflows.length} workflows (total: ${allWorkflows.length})`);
|
|
|
|
// If we got less than page size, we've reached the last page
|
|
if (workflows.length < pageSize) {
|
|
console.error('[Workflow] Last page reached');
|
|
break;
|
|
}
|
|
|
|
offset += pageSize;
|
|
}
|
|
|
|
// Update cache
|
|
workflowCache = allWorkflows;
|
|
cacheTimestamp = Date.now();
|
|
|
|
console.error(`[Workflow] Successfully cached ${allWorkflows.length} workflows`);
|
|
return allWorkflows;
|
|
} catch (error) {
|
|
console.error('[Workflow] Error fetching workflows:', error.message);
|
|
throw new Error(`Failed to fetch workflows: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Search workflows by query string
|
|
* @param {string} query - Search query (matches name, description, etc.)
|
|
* @param {string|null} category - Optional category filter
|
|
* @param {number} limit - Maximum results to return
|
|
*/
|
|
async function searchWorkflows(query, category = null, limit = 50) {
|
|
const workflows = await fetchAllWorkflows();
|
|
|
|
let results = workflows;
|
|
|
|
// Filter by query if provided
|
|
if (query) {
|
|
const lowerQuery = query.toLowerCase();
|
|
results = results.filter(w => {
|
|
const name = (w.name || w.Name || '').toLowerCase();
|
|
const description = (w.description || w.Description || '').toLowerCase();
|
|
const code = (w.code || w.Code || '').toLowerCase();
|
|
|
|
return name.includes(lowerQuery) ||
|
|
description.includes(lowerQuery) ||
|
|
code.includes(lowerQuery);
|
|
});
|
|
}
|
|
|
|
// Filter by category if provided
|
|
if (category) {
|
|
const lowerCategory = category.toLowerCase();
|
|
results = results.filter(w => {
|
|
const wfCategory = (w.category || w.Category || '').toLowerCase();
|
|
return wfCategory.includes(lowerCategory);
|
|
});
|
|
}
|
|
|
|
// Limit results
|
|
return results.slice(0, limit);
|
|
}
|
|
|
|
/**
|
|
* Get workflow details by ID
|
|
* @param {string|number} workflowId - Workflow ID
|
|
*/
|
|
async function getWorkflowDetails(workflowId) {
|
|
const workflows = await fetchAllWorkflows();
|
|
|
|
// Try to find by Id, id, Code, code, Name, or name
|
|
const workflow = workflows.find(w =>
|
|
w.id === workflowId ||
|
|
w.Id === workflowId ||
|
|
w.id === parseInt(workflowId) ||
|
|
w.Id === parseInt(workflowId) ||
|
|
w.Code === workflowId ||
|
|
w.code === workflowId ||
|
|
w.Name === workflowId ||
|
|
w.name === workflowId
|
|
);
|
|
|
|
if (!workflow) {
|
|
throw new Error(`Workflow not found: ${workflowId}`);
|
|
}
|
|
|
|
return workflow;
|
|
}
|
|
|
|
/**
|
|
* List all workflow categories
|
|
*/
|
|
async function listWorkflowCategories() {
|
|
const workflows = await fetchAllWorkflows();
|
|
|
|
// Extract unique categories (try both lowercase and uppercase)
|
|
const categories = new Set();
|
|
workflows.forEach(w => {
|
|
const category = w.category || w.Category;
|
|
if (category) {
|
|
categories.add(category);
|
|
}
|
|
});
|
|
|
|
// Sort alphabetically
|
|
return Array.from(categories).sort();
|
|
}
|
|
|
|
/**
|
|
* Get workflow statistics
|
|
*/
|
|
async function getWorkflowStats() {
|
|
const workflows = await fetchAllWorkflows();
|
|
const categories = await listWorkflowCategories();
|
|
|
|
// Count workflows per category
|
|
const categoryCounts = {};
|
|
workflows.forEach(w => {
|
|
const cat = w.category || w.Category || 'Uncategorized';
|
|
categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
|
|
});
|
|
|
|
return {
|
|
total: workflows.length,
|
|
categories: categories.length,
|
|
categoryCounts,
|
|
cacheAge: cacheTimestamp ? Math.floor((Date.now() - cacheTimestamp) / 1000) : null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Clear workflow cache (force refresh on next request)
|
|
*/
|
|
function clearCache() {
|
|
workflowCache = null;
|
|
cacheTimestamp = null;
|
|
console.error('[Workflow] Cache cleared');
|
|
}
|
|
|
|
/**
|
|
* Get cache status
|
|
*/
|
|
function getCacheStatus() {
|
|
return {
|
|
cached: workflowCache !== null,
|
|
count: workflowCache ? workflowCache.length : 0,
|
|
timestamp: cacheTimestamp,
|
|
age: cacheTimestamp ? Math.floor((Date.now() - cacheTimestamp) / 1000) : null,
|
|
valid: isCacheValid()
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
fetchAllWorkflows,
|
|
searchWorkflows,
|
|
getWorkflowDetails,
|
|
listWorkflowCategories,
|
|
getWorkflowStats,
|
|
clearCache,
|
|
getCacheStatus
|
|
};
|