snow-flow
Version:
Snow-Flow v3.2.0: Complete ServiceNow Enterprise Suite with 180+ MCP Tools. ATF Testing, Knowledge Management, Service Catalog, Change Management with CAB scheduling, Virtual Agent chatbots with NLU, Performance Analytics KPIs, Flow Designer automation, A
449 lines • 19.5 kB
JavaScript
"use strict";
/**
* Scope Manager for ServiceNow Artifact Deployment
*
* Centralized management of deployment scopes, providing intelligent
* scope selection and management capabilities.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScopeManager = void 0;
const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
const logger_js_1 = require("../utils/logger.js");
class ScopeManager {
constructor(options = {}) {
this.scopeCache = new Map();
this.strategy = new global_scope_strategy_js_1.GlobalScopeStrategy();
this.client = new servicenow_client_js_1.ServiceNowClient();
this.logger = new logger_js_1.Logger('ScopeManager');
this.options = {
defaultScope: global_scope_strategy_js_1.ScopeType.GLOBAL,
allowFallback: true,
validatePermissions: true,
enableMigration: false,
...options
};
this.logger.info('ScopeManager initialized', { options: this.options });
}
/**
* Make intelligent scope decision based on context
*/
async makeScopeDecision(context) {
const cacheKey = this.generateCacheKey(context);
// Check cache first
if (this.scopeCache.has(cacheKey)) {
const cached = this.scopeCache.get(cacheKey);
this.logger.info('Using cached scope decision', { selectedScope: cached.selectedScope });
return cached;
}
this.logger.info('Making scope decision', {
artifactType: context.artifactType,
artifactName: context.artifactData.name,
environmentType: context.environmentType
});
// Step 1: Analyze artifact requirements
const scopeConfig = await this.strategy.analyzeScopeRequirements(context.artifactType, context.artifactData);
// Step 2: Apply user preferences and project scope
const adjustedConfig = this.applyUserPreferences(scopeConfig, context);
// Step 3: Validate permissions if enabled
let validationResult = { isValid: true, issues: [], warnings: [] };
if (this.options.validatePermissions) {
validationResult = await this.strategy.validateScopePermissions(adjustedConfig);
}
// Step 4: Make final decision
const decision = this.makeFinalDecision(adjustedConfig, validationResult, context);
// Step 5: Cache the decision
this.scopeCache.set(cacheKey, decision);
this.logger.info('Scope decision made', {
selectedScope: decision.selectedScope,
confidence: decision.confidence,
rationale: decision.rationale
});
return decision;
}
/**
* Deploy artifact using intelligent scope management
*/
async deployWithScopeManagement(context) {
this.logger.info('Starting scope-managed deployment', {
artifactType: context.artifactType,
artifactName: context.artifactData.name
});
// Make scope decision
const decision = await this.makeScopeDecision(context);
// Prepare deployment configuration
const deploymentConfig = {
type: decision.selectedScope,
fallbackToGlobal: this.options.allowFallback && decision.fallbackScope !== undefined,
permissions: decision.validationResult.permissions || []
};
// Add environment-specific configurations
if (context.environmentType === 'production') {
deploymentConfig.permissions?.push('production_deployment');
}
// Add compliance requirements
if (context.complianceRequirements) {
deploymentConfig.permissions?.push(...context.complianceRequirements);
}
// Execute deployment
const result = await this.strategy.deployWithGlobalScope(context.artifactType, context.artifactData, deploymentConfig);
// Log deployment outcome
this.logger.info('Scope-managed deployment completed', {
success: result.success,
actualScope: result.scope,
fallbackApplied: result.fallbackApplied
});
return result;
}
/**
* Migrate existing artifacts to optimal scope
*/
async migrateArtifactsToOptimalScope(artifacts) {
if (!this.options.enableMigration) {
throw new Error('Migration is not enabled in scope manager options');
}
this.logger.info('Starting artifact migration', { count: artifacts.length });
const migrationResults = {
totalArtifacts: artifacts.length,
successful: 0,
failed: 0,
skipped: 0,
details: []
};
for (const artifact of artifacts) {
try {
const context = {
artifactType: artifact.sys_class_name || artifact.type,
artifactData: artifact,
environmentType: 'development' // Safe default for migration
};
const decision = await this.makeScopeDecision(context);
// Only migrate if the optimal scope is different from current
if (this.shouldMigrateArtifact(artifact, decision)) {
const migrationResult = await this.migrateArtifact(artifact, decision);
migrationResults.details.push(migrationResult);
if (migrationResult.success) {
migrationResults.successful++;
}
else {
migrationResults.failed++;
}
}
else {
migrationResults.skipped++;
migrationResults.details.push({
artifactId: artifact.sys_id,
name: artifact.name,
currentScope: artifact.sys_scope,
recommendedScope: decision.selectedScope,
action: 'skipped',
reason: 'Already in optimal scope'
});
}
}
catch (error) {
migrationResults.failed++;
migrationResults.details.push({
artifactId: artifact.sys_id,
name: artifact.name,
action: 'failed',
error: error instanceof Error ? error.message : String(error)
});
}
}
this.logger.info('Migration completed', {
successful: migrationResults.successful,
failed: migrationResults.failed,
skipped: migrationResults.skipped
});
return migrationResults;
}
/**
* Get scope recommendations for a project
*/
async getProjectScopeRecommendations(projectArtifacts) {
this.logger.info('Generating project scope recommendations', {
artifactCount: projectArtifacts.length
});
const recommendations = {
overallScope: global_scope_strategy_js_1.ScopeType.GLOBAL,
confidence: 0,
artifactBreakdown: {
global: 0,
application: 0,
mixed: 0
},
recommendations: [],
risks: [],
benefits: []
};
const decisions = [];
// Analyze each artifact
for (const artifact of projectArtifacts) {
const context = {
artifactType: artifact.sys_class_name || artifact.type,
artifactData: artifact
};
const decision = await this.makeScopeDecision(context);
decisions.push(decision);
if (decision.selectedScope === global_scope_strategy_js_1.ScopeType.GLOBAL) {
recommendations.artifactBreakdown.global++;
}
else if (decision.selectedScope === global_scope_strategy_js_1.ScopeType.APPLICATION) {
recommendations.artifactBreakdown.application++;
}
else {
recommendations.artifactBreakdown.mixed++;
}
}
// Calculate overall recommendation
const globalRatio = recommendations.artifactBreakdown.global / projectArtifacts.length;
const applicationRatio = recommendations.artifactBreakdown.application / projectArtifacts.length;
if (globalRatio > 0.6) {
recommendations.overallScope = global_scope_strategy_js_1.ScopeType.GLOBAL;
recommendations.confidence = globalRatio;
recommendations.recommendations.push('Deploy entire project to global scope for consistency');
recommendations.benefits.push('Simplified deployment and maintenance');
recommendations.benefits.push('Better cross-application integration');
}
else if (applicationRatio > 0.6) {
recommendations.overallScope = global_scope_strategy_js_1.ScopeType.APPLICATION;
recommendations.confidence = applicationRatio;
recommendations.recommendations.push('Deploy project to dedicated application scope');
recommendations.benefits.push('Better isolation and security');
recommendations.benefits.push('Easier application lifecycle management');
}
else {
recommendations.overallScope = global_scope_strategy_js_1.ScopeType.AUTO;
recommendations.confidence = 0.5;
recommendations.recommendations.push('Use mixed scope strategy - deploy artifacts to optimal individual scopes');
recommendations.risks.push('Mixed scope may complicate maintenance');
}
// Add specific recommendations
if (recommendations.artifactBreakdown.global > 0) {
recommendations.recommendations.push('Ensure global scope permissions are available');
}
if (recommendations.artifactBreakdown.application > 0) {
recommendations.recommendations.push('Consider creating dedicated application scope');
}
return recommendations;
}
/**
* Validate scope configuration
*/
async validateScopeConfiguration(config) {
this.logger.info('Validating scope configuration', { type: config.type });
const validation = {
isValid: true,
issues: [],
warnings: [],
recommendations: []
};
// Validate scope type
if (!Object.values(global_scope_strategy_js_1.ScopeType).includes(config.type)) {
validation.isValid = false;
validation.issues.push(`Invalid scope type: ${config.type}`);
}
// Validate application scope requirements
if (config.type === global_scope_strategy_js_1.ScopeType.APPLICATION) {
if (!config.applicationId && !config.applicationName) {
validation.warnings.push('Application scope specified but no application ID or name provided');
}
}
// Validate permissions
if (config.permissions && config.permissions.length > 0) {
const permissionValidation = await this.validatePermissions(config.permissions);
if (!permissionValidation.isValid) {
validation.isValid = false;
validation.issues.push(...permissionValidation.issues);
}
}
// Validate global domain
if (config.type === global_scope_strategy_js_1.ScopeType.GLOBAL && config.globalDomain !== 'global') {
validation.warnings.push('Global scope should use "global" domain');
}
return validation;
}
/**
* Clear scope cache
*/
clearScopeCache() {
this.scopeCache.clear();
this.logger.info('Scope cache cleared');
}
/**
* Get scope statistics
*/
getScopeStatistics() {
const stats = {
cacheSize: this.scopeCache.size,
scopeDistribution: {
global: 0,
application: 0,
auto: 0
},
averageConfidence: 0
};
let totalConfidence = 0;
for (const decision of this.scopeCache.values()) {
totalConfidence += decision.confidence;
if (decision.selectedScope === global_scope_strategy_js_1.ScopeType.GLOBAL) {
stats.scopeDistribution.global++;
}
else if (decision.selectedScope === global_scope_strategy_js_1.ScopeType.APPLICATION) {
stats.scopeDistribution.application++;
}
else {
stats.scopeDistribution.auto++;
}
}
stats.averageConfidence = this.scopeCache.size > 0 ? totalConfidence / this.scopeCache.size : 0;
return stats;
}
/**
* Private helper methods
*/
generateCacheKey(context) {
return `${context.artifactType}:${context.artifactData.name}:${context.environmentType || 'default'}`;
}
applyUserPreferences(config, context) {
if (!context.userPreferences) {
return config;
}
// Override with user preferences
return {
...config,
...context.userPreferences
};
}
makeFinalDecision(config, validation, context) {
let selectedScope = config.type;
let confidence = 0.8;
let rationale = 'Based on artifact _analysis';
let fallbackScope;
// Apply validation results
if (!validation.isValid) {
if (validation.canCreateGlobal && config.type === global_scope_strategy_js_1.ScopeType.APPLICATION) {
selectedScope = global_scope_strategy_js_1.ScopeType.GLOBAL;
fallbackScope = global_scope_strategy_js_1.ScopeType.APPLICATION;
confidence = 0.6;
rationale = 'Fallback to global scope due to permission issues';
}
else if (validation.canCreateScoped && config.type === global_scope_strategy_js_1.ScopeType.GLOBAL) {
selectedScope = global_scope_strategy_js_1.ScopeType.APPLICATION;
fallbackScope = global_scope_strategy_js_1.ScopeType.GLOBAL;
confidence = 0.6;
rationale = 'Fallback to application scope due to permission issues';
}
else {
confidence = 0.3;
rationale = 'Limited options due to permission constraints';
}
}
// Apply environment-specific rules
if (context.environmentType === 'production') {
// Production deployments prefer global scope for stability
if (selectedScope === global_scope_strategy_js_1.ScopeType.APPLICATION && validation.canCreateGlobal) {
selectedScope = global_scope_strategy_js_1.ScopeType.GLOBAL;
confidence = Math.max(confidence, 0.7);
rationale += ' (production environment preference)';
}
}
// Apply project scope preferences
if (context.projectScope) {
if (context.projectScope === global_scope_strategy_js_1.ScopeType.GLOBAL && validation.canCreateGlobal) {
selectedScope = global_scope_strategy_js_1.ScopeType.GLOBAL;
confidence = Math.max(confidence, 0.8);
rationale += ' (project scope preference)';
}
else if (context.projectScope === global_scope_strategy_js_1.ScopeType.APPLICATION && validation.canCreateScoped) {
selectedScope = global_scope_strategy_js_1.ScopeType.APPLICATION;
confidence = Math.max(confidence, 0.8);
rationale += ' (project scope preference)';
}
}
const recommendations = [];
// Generate recommendations
if (confidence < 0.5) {
recommendations.push('Consider reviewing permissions or scope configuration');
}
if (fallbackScope) {
recommendations.push(`Consider ${fallbackScope} scope as alternative`);
}
if (validation.warnings && validation.warnings.length > 0) {
recommendations.push(...validation.warnings);
}
return {
selectedScope,
confidence,
rationale,
fallbackScope,
validationResult: validation,
recommendations
};
}
shouldMigrateArtifact(artifact, decision) {
const currentScope = artifact.sys_scope;
const recommendedScope = decision.selectedScope;
// Don't migrate if confidence is too low
if (decision.confidence < 0.7) {
return false;
}
// Don't migrate if already in optimal scope
if (currentScope === 'global' && recommendedScope === global_scope_strategy_js_1.ScopeType.GLOBAL) {
return false;
}
if (currentScope !== 'global' && recommendedScope === global_scope_strategy_js_1.ScopeType.APPLICATION) {
return false;
}
return true;
}
async migrateArtifact(artifact, decision) {
// This is a simplified migration - in practice, this would involve
// complex operations like dependency checking, data migration, etc.
const migrationResult = {
artifactId: artifact.sys_id,
name: artifact.name,
currentScope: artifact.sys_scope,
targetScope: decision.selectedScope,
success: false,
action: 'migrate',
details: {}
};
try {
// Create new artifact in target scope
const newArtifactData = {
...artifact,
sys_scope: decision.selectedScope === global_scope_strategy_js_1.ScopeType.GLOBAL ? 'global' : decision.selectedScope,
name: `${artifact.name}_migrated`
// 🔧 TEST-001 FIX: Remove sys_id property instead of setting to undefined
// This prevents "sys_id undefined" errors in testing tools
};
// 🔧 TEST-001 FIX: Explicitly delete sys_id so ServiceNow generates a new one
delete newArtifactData.sys_id;
const deploymentResult = await this.strategy.deployWithGlobalScope(artifact.sys_class_name || artifact.type, newArtifactData, { type: decision.selectedScope });
migrationResult.success = deploymentResult.success;
migrationResult.details = deploymentResult;
// In a real implementation, you would also:
// 1. Update references to the old artifact
// 2. Deactivate or delete the old artifact
// 3. Verify the migration worked correctly
}
catch (error) {
migrationResult.success = false;
migrationResult.details = { error: error instanceof Error ? error.message : String(error) };
}
return migrationResult;
}
async validatePermissions(permissions) {
// Simplified permission validation
// In practice, this would check actual ServiceNow permissions
return {
isValid: true,
issues: [],
warnings: []
};
}
}
exports.ScopeManager = ScopeManager;
//# sourceMappingURL=scope-manager.js.map