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
292 lines • 10.8 kB
JavaScript
"use strict";
/**
* Artifact Tracker - Manages consistent sys_id tracking and validation
* Solves the sys_id inconsistency problem identified in feedback
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.artifactTracker = exports.ArtifactTracker = void 0;
const logger_js_1 = require("./logger.js");
const servicenow_client_js_1 = require("./servicenow-client.js");
class ArtifactTracker {
constructor() {
this.sessions = new Map();
this.currentSessionId = null;
this.logger = new logger_js_1.Logger('ArtifactTracker');
this.client = new servicenow_client_js_1.ServiceNowClient();
}
/**
* Create or resume a tracking session
*/
startSession(sessionId) {
const id = sessionId || this.generateSessionId();
if (!this.sessions.has(id)) {
this.sessions.set(id, {
sessionId: id,
artifacts: new Map(),
createdAt: new Date(),
lastAccessed: new Date()
});
this.logger.info(`Started new artifact tracking session: ${id}`);
}
else {
const session = this.sessions.get(id);
session.lastAccessed = new Date();
this.logger.info(`Resumed artifact tracking session: ${id}`);
}
this.currentSessionId = id;
return id;
}
/**
* Track a new artifact
*/
trackArtifact(sys_id, table, name, type, operation = 'create') {
if (!this.currentSessionId) {
this.startSession();
}
const session = this.sessions.get(this.currentSessionId);
// Check for existing tracking
const existingKey = this.findArtifactKey(sys_id, name, type);
if (existingKey && existingKey !== sys_id) {
this.logger.warn(`Sys_id inconsistency detected! Found existing artifact with different sys_id:`, {
new_sys_id: sys_id,
existing_sys_id: existingKey,
name,
type
});
}
const artifact = {
sys_id,
table,
name,
type,
status: operation === 'create' ? 'pending' : 'modified',
operations: [{
operation,
timestamp: new Date(),
success: true,
details: `Artifact tracked for ${operation}`
}],
lastValidated: new Date()
};
session.artifacts.set(sys_id, artifact);
this.logger.info(`Tracking artifact: ${name} (${sys_id}) in table ${table}`);
return artifact;
}
/**
* Validate that a sys_id actually exists in ServiceNow
*/
async validateArtifact(sys_id) {
if (!this.currentSessionId) {
return false;
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return false;
}
const artifact = session.artifacts.get(sys_id);
if (!artifact) {
this.logger.warn(`Cannot validate unknown artifact: ${sys_id}`);
return false;
}
try {
this.logger.info(`Validating artifact ${sys_id} in table ${artifact.table}`);
// getRecord returns the data directly or throws an error
const data = await this.client.getRecord(artifact.table, sys_id);
// If we got here without throwing, the record exists
const isValid = !!data;
// Update tracking
artifact.operations.push({
operation: 'validate',
timestamp: new Date(),
success: isValid,
details: isValid ? 'Artifact exists in ServiceNow' : 'Artifact not found in ServiceNow',
error: undefined
});
artifact.lastValidated = new Date();
return isValid;
}
catch (error) {
// Handle specific error cases
const errorMessage = String(error);
let validationDetails = 'Validation error';
let skipErrorStatus = false;
// Check if it's a permission error (403) or authentication error (401)
if (errorMessage.includes('403') || errorMessage.includes('Forbidden')) {
validationDetails = 'Unable to validate - insufficient read permissions';
skipErrorStatus = true; // Don't mark as error since deployment succeeded
this.logger.warn(`Permission issue during validation for ${sys_id}, but deployment was successful`);
}
else if (errorMessage.includes('401') || errorMessage.includes('Unauthorized')) {
validationDetails = 'Unable to validate - authentication issue';
skipErrorStatus = true;
this.logger.warn(`Authentication issue during validation for ${sys_id}, but deployment was successful`);
}
else {
this.logger.error(`Error validating artifact ${sys_id}:`, error);
}
artifact.operations.push({
operation: 'validate',
timestamp: new Date(),
success: false,
details: validationDetails,
error: errorMessage
});
// Only mark as error if it's not a permission issue and deployment already succeeded
if (!skipErrorStatus && artifact.status !== 'deployed') {
artifact.status = 'error';
}
// For permission/auth errors on successfully deployed artifacts, consider it valid
if (skipErrorStatus && artifact.status === 'deployed') {
return true;
}
return false;
}
}
/**
* Record an operation on a tracked artifact
*/
recordOperation(sys_id, operation, success, details, error) {
if (!this.currentSessionId) {
return;
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return;
}
const artifact = session.artifacts.get(sys_id);
if (!artifact) {
this.logger.warn(`Cannot record operation for unknown artifact: ${sys_id}`);
return;
}
artifact.operations.push({
operation,
timestamp: new Date(),
success,
details,
error
});
// Update status
if (success) {
artifact.status = operation === 'create' ? 'deployed' : 'modified';
}
else {
artifact.status = 'error';
}
this.logger.info(`Recorded ${operation} operation for ${sys_id}: ${success ? 'success' : 'failure'}`);
}
/**
* Get all tracked artifacts in current session
*/
getTrackedArtifacts() {
if (!this.currentSessionId) {
return [];
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return [];
}
return Array.from(session.artifacts.values());
}
/**
* Get specific artifact by sys_id
*/
getArtifact(sys_id) {
if (!this.currentSessionId) {
return null;
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return null;
}
return session.artifacts.get(sys_id) || null;
}
/**
* Find sys_id inconsistencies
*/
findInconsistencies() {
const inconsistencies = [];
if (!this.currentSessionId) {
return inconsistencies;
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return inconsistencies;
}
const artifacts = Array.from(session.artifacts.values());
const nameTypeMap = new Map();
// Group by name + type
for (const artifact of artifacts) {
const key = `${artifact.name}:${artifact.type}`;
if (!nameTypeMap.has(key)) {
nameTypeMap.set(key, []);
}
nameTypeMap.get(key).push(artifact);
}
// Find duplicates
for (const [key, duplicates] of nameTypeMap.entries()) {
if (duplicates.length > 1) {
inconsistencies.push({
artifacts: duplicates,
issue: `Multiple sys_ids for same artifact: ${key}`
});
}
}
return inconsistencies;
}
/**
* Generate session summary for debugging
*/
getSessionSummary() {
if (!this.currentSessionId) {
return { error: 'No active session' };
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return { error: 'Session not found' };
}
const artifacts = Array.from(session.artifacts.values());
const summary = {
sessionId: this.currentSessionId,
artifactCount: artifacts.length,
statusCounts: {
pending: artifacts.filter(a => a.status === 'pending').length,
deployed: artifacts.filter(a => a.status === 'deployed').length,
modified: artifacts.filter(a => a.status === 'modified').length,
error: artifacts.filter(a => a.status === 'error').length
},
inconsistencies: this.findInconsistencies(),
artifacts: artifacts.map(a => ({
sys_id: a.sys_id,
name: a.name,
type: a.type,
status: a.status,
operationCount: a.operations.length,
lastValidated: a.lastValidated
}))
};
return summary;
}
findArtifactKey(sys_id, name, type) {
if (!this.currentSessionId) {
return null;
}
const session = this.sessions.get(this.currentSessionId);
if (!session) {
return null;
}
// Look for existing artifact with same name and type
for (const [existingSysId, artifact] of session.artifacts.entries()) {
if (artifact.name === name && artifact.type === type && existingSysId !== sys_id) {
return existingSysId;
}
}
return null;
}
generateSessionId() {
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
exports.ArtifactTracker = ArtifactTracker;
// Global instance for session management
exports.artifactTracker = new ArtifactTracker();
//# sourceMappingURL=artifact-tracker.js.map