@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
160 lines • 7.29 kB
JavaScript
/**
* Compare Environments Tool - Individual Module
* @description Compares configuration and entities between different environments
* @since 2025-08-04
* @author Tool Modularization Team
*
* Migration Status: COMPLETED
* Original Method: OptimizelyMCPTools.compareEnvironments
* Complexity: LOW
* Dependencies: storage.query, logger, errorMapper, cacheManager
*/
/**
* Creates the Compare Environments tool with injected dependencies
* @param deps - Injected dependencies (storage, logger, errorMapper, etc.)
* @returns Tool definition with handler
*/
export function createCompareEnvironmentsTool(deps) {
return {
name: 'compare_environments',
requiresCache: true,
category: 'analytics',
description: 'Compares configuration and entities between different environments',
handler: async (args) => {
try {
const { project_id, flag_key, environments: envFilter } = args;
// Get all environments if not specified
let environments;
if (envFilter && envFilter.length > 0) {
environments = envFilter;
}
else {
const envQuery = await deps.storage.query(`
SELECT key FROM environments
WHERE project_id = ?
ORDER BY priority, key
`, [project_id]);
environments = envQuery.map(e => e.key);
}
if (environments.length < 2) {
throw deps.errorMapper.toMCPError(new Error('At least 2 environments are required for comparison'), { operation: 'Compare environments', metadata: { environments: args.environments } });
}
// Build query for flags
let flagQuery = `
SELECT DISTINCT f.key, f.name
FROM flags f
WHERE f.project_id = ? AND f.archived = 0
`;
const queryParams = [project_id];
if (flag_key) {
flagQuery += ' AND f.key = ?';
queryParams.push(flag_key);
}
const flags = await deps.storage.query(flagQuery, queryParams);
const flagsWithDifferences = [];
const environmentOnlyFlags = {};
environments.forEach(env => { environmentOnlyFlags[env] = 0; });
// Compare each flag across environments
for (const flag of flags) {
const envData = {};
const differences = [];
// Get flag environment data for all environments
const feQuery = await deps.storage.query(`
SELECT environment_key, enabled, data_json
FROM flag_environments
WHERE project_id = ? AND flag_key = ?
AND environment_key IN (${environments.map(() => '?').join(',')})
`, [project_id, flag.key, ...environments]);
// Build environment data map
for (const fe of feQuery) {
let rulesetData = {};
try {
if (fe.data_json)
rulesetData = JSON.parse(fe.data_json);
}
catch (e) {
deps.logger.warn('Could not parse ruleset', { flagKey: flag.key, environmentKey: fe.environment_key });
}
envData[fe.environment_key] = {
enabled: Boolean(fe.enabled),
ruleset: rulesetData
};
}
// Add missing environments with default values
for (const env of environments) {
if (!envData[env]) {
envData[env] = { enabled: false, ruleset: null };
}
}
// Check for differences in enabled state
const enabledValues = {};
let hasEnabledDiff = false;
let firstEnabled = null;
for (const env of environments) {
enabledValues[env] = envData[env].enabled;
if (firstEnabled === null) {
firstEnabled = envData[env].enabled;
}
else if (firstEnabled !== envData[env].enabled) {
hasEnabledDiff = true;
}
}
if (hasEnabledDiff) {
differences.push({
field: 'enabled',
values: enabledValues
});
}
// Check for differences in rulesets (simplified comparison)
const rulesetHashes = {};
let hasRulesetDiff = false;
let firstHash = null;
for (const env of environments) {
const ruleset = envData[env].ruleset;
const hash = ruleset ? JSON.stringify(ruleset).substring(0, 50) + '...' : 'none';
rulesetHashes[env] = hash;
if (firstHash === null) {
firstHash = hash;
}
else if (firstHash !== hash) {
hasRulesetDiff = true;
}
}
if (hasRulesetDiff) {
differences.push({
field: 'ruleset',
values: rulesetHashes
});
}
// Add to results if differences found
if (differences.length > 0) {
flagsWithDifferences.push({
key: flag.key,
name: flag.name,
differences
});
}
// Count environment-only flags
const configuredEnvs = environments.filter(env => envData[env].enabled || envData[env].ruleset);
if (configuredEnvs.length === 1) {
environmentOnlyFlags[configuredEnvs[0]]++;
}
}
return {
environments,
flags: flagsWithDifferences,
summary: {
total_flags: flags.length,
flags_with_differences: flagsWithDifferences.length,
environment_only_flags: environmentOnlyFlags
}
};
}
catch (error) {
deps.logger.error({ error: error.message, stack: error.stack }, 'OptimizelyMCPTools.compareEnvironments failed');
throw deps.errorMapper.toMCPError(error, 'Failed to compare environments');
}
}
};
}
//# sourceMappingURL=CompareEnvironments.js.map