From b54c563053d111183777f8899515a852d2dd68de Mon Sep 17 00:00:00 2001 From: Ashish Jullia Date: Wed, 23 Apr 2025 18:19:00 +0530 Subject: [PATCH] projects - Updated updateProjectName to handle project key's actual value update for an update project name and then for deleteProject's bug only delete those stages and KVs and not all --- modules/projects.js | 163 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 14 deletions(-) diff --git a/modules/projects.js b/modules/projects.js index 4d11481..b5b0f03 100644 --- a/modules/projects.js +++ b/modules/projects.js @@ -31,27 +31,162 @@ module.exports.createProject = async ({ team, project }) => { return projectData; }; -module.exports.updateProjectName = async ({ project, name }) => { - const projectData = await PROJECTS.get(project, 'json'); - if (!projectData) { - throw new Error(`Project with key "${project}" does not exist.`); +module.exports.updateProjectName = async ({ project: oldProjectKey, name: newProjectName }) => { + console.log(`Starting updateProjectName for old key: ${oldProjectKey}, new name: ${newProjectName}`); + + // 1. Fetch Old Project Data & Validate + let oldProjectData; + try { + oldProjectData = await PROJECTS.get(oldProjectKey, 'json'); + if (!oldProjectData) { + throw new Error(`Project with key "${oldProjectKey}" does not exist.`); + } + } catch (error) { + console.error(`Error fetching project "${oldProjectKey}":`, error); + throw new Error(`Failed to retrieve project data for key "${oldProjectKey}". Original error: ${error.message}`); } - const teamKey = projectData.team; - const newProjectKey = `${teamKey}::${name}`; - const existingProject = await PROJECTS.get(newProjectKey, 'json'); - if (existingProject) { - throw new Error(`Project with name "${name}" already exists in team "${teamKey}".`); + + const teamKey = oldProjectData.team; + const oldProjectName = oldProjectData.project; // Get the actual old name from data + + // Early exit if name hasn't changed + if (oldProjectName === newProjectName) { + console.log(`New name "${newProjectName}" is the same as the old name. No update needed.`); + return oldProjectData; } - projectData.project = name; - await PROJECTS.delete(project); - await PROJECTS.put(newProjectKey, JSON.stringify(projectData)); - return projectData; + + const newProjectKey = `${teamKey}::${newProjectName}`; + + // 2. Check for New Name Conflict (Ensure target project key is free) + try { + const existingProject = await PROJECTS.get(newProjectKey); // Check existence is enough + if (existingProject !== null) { // Check if the key exists + throw new Error(`Project with name "${newProjectName}" already exists in team "${teamKey}" (key: ${newProjectKey}).`); + } + } catch (error) { + if (error.message.includes('already exists')) { + console.error(`Conflict check failed: New project key "${newProjectKey}" already exists.`, error); + throw error; + } + console.warn(`Potential issue during new key check for "${newProjectKey}" (but not a conflict error):`, error); + } + + // 3. List Associated Stages based on the OLD project name + const oldStagePrefix = `${teamKey}::${oldProjectName}::`; + let oldStageKeys = []; + try { + const listedStages = await STAGES.list({ prefix: oldStagePrefix }); + // Make sure listedStages and listedStages.keys exist before accessing + if (listedStages && listedStages.keys) { + oldStageKeys = listedStages.keys; + } + console.log(`Found ${oldStageKeys.length} stages with prefix "${oldStagePrefix}" to migrate.`); + } catch (error) { + console.error(`Failed to list stages for old project "${oldProjectKey}" with prefix "${oldStagePrefix}":`, error); + throw new Error(`Error listing stages for migration for project "${oldProjectName}". Original error: ${error.message}`); + } + + // 4. Migrate Related STAGES and KV Data using String.replace + if (oldStageKeys.length > 0) { + console.log(`Starting migration of ${oldStageKeys.length} stages and associated KV pairs...`); + const newStagePrefix = `${teamKey}::${newProjectName}::`; // Define new prefix for replacement + + try { + const migrationPromises = oldStageKeys.map(async ({ name: oldStageKey }) => { + + if (!oldStageKey.startsWith(oldStagePrefix)) { + console.error(` - Error: Stage key "${oldStageKey}" does not start with expected prefix "${oldStagePrefix}". Skipping migration for this key.`); + return; + } + const newStageKey = oldStageKey.replace(oldStagePrefix, newStagePrefix); + + console.log(`Migrating stage: ${oldStageKey} -> ${newStageKey}`); + + let stageDataJson = null; + try { + stageDataJson = await STAGES.get(oldStageKey, 'json'); // Fetch as JSON object + } catch (getStageError) { + console.warn(` - Warning: Failed to get STAGE data for key ${oldStageKey}. Skipping STAGE migration. Error: ${getStageError.message}`); + } + + if (stageDataJson !== null) { + const updatedStageData = { + ...stageDataJson, + project: newProjectKey, + key: newStageKey, + updated: new Date().toISOString() + }; + + try { + await STAGES.put(newStageKey, JSON.stringify(updatedStageData)); + await STAGES.delete(oldStageKey); + console.log(` - Stage entry migrated (data updated): ${oldStageKey} -> ${newStageKey}`); + } catch (putDeleteStageError) { + console.error(` - Error migrating STAGE entry ${oldStageKey} -> ${newStageKey}. Error: ${putDeleteStageError.message}`); + } + } else if (!getStageError) { + console.warn(` - Warning: No JSON data found for stage key ${oldStageKey} during migration.`); + } + + let kvData = null; + let getKvError = null; + try { + kvData = await KV.get(oldStageKey); + } catch(err) { + getKvError = err; + console.warn(` - Warning: Failed to get KV data for key ${oldStageKey}. Skipping KV migration. Error: ${err.message}`); + } + + if (kvData !== null) { + try { + await KV.put(newStageKey, kvData); + await KV.delete(oldStageKey); + console.log(` - KV entry migrated (raw value): ${oldStageKey} -> ${newStageKey}`); + } catch (putDeleteKvError) { + console.error(` - Error migrating KV entry ${oldStageKey} -> ${newStageKey}. Error: ${putDeleteKvError.message}`); + } + } else if (!getKvError) { + } + }); + + await Promise.all(migrationPromises); + console.log(`Finished migration attempt for ${oldStageKeys.length} stages and associated KV pairs.`); + } catch (error) { + console.error(`Critical failure during the migration process for project "${oldProjectName}". Error:`, error); + throw new Error(`Error migrating stages/KV data for project "${oldProjectName}". State might be inconsistent. Original error: ${error.message}`); + } + } else { + console.log(`No stages found with prefix "${oldStagePrefix}". Skipping migration.`); + } + + // 5. Update the Main Project Entry + const updatedProjectData = { + ...oldProjectData, // Preserve other fields from the original data + project: newProjectName, + key: newProjectKey, + updated: new Date().toISOString(), + }; + + try { + await PROJECTS.put(newProjectKey, JSON.stringify(updatedProjectData)); + console.log(`Successfully created new project entry with key: ${newProjectKey}`); + + await PROJECTS.delete(oldProjectKey); + console.log(`Successfully deleted old project entry with key: ${oldProjectKey}`); + + } catch (error) { + console.error(`Failed to update project entry from "${oldProjectKey}" to "${newProjectKey}". Error:`, error); + throw new Error(`Error finalizing project rename from "${oldProjectName}" to "${newProjectName}". Original error: ${error.message}`); + } + console.log(`Successfully updated project name to "${newProjectName}" with key "${newProjectKey}".`); + return updatedProjectData; }; module.exports.deleteProject = async ({ project, team }) => { let stages = []; + const projectName = project.split('::')[1]; try { - const stageKeys = await STAGES.list({ prefix: `${team}::` }); + const stageKeys = await STAGES.list({ prefix: `${team}::${projectName}` }); console.log("deleteProject:stageKeys....", stageKeys); stages = stageKeys.keys; if (stages.length === 0) {