import { get, isEqual, isArray } from 'lodash'; import { PageDSL, isValidStepDef, ValidMultiStepDef, StepDef } from '../types/index.js'; import { FlattenedWidgetProps } from '../utils/normalize.js'; import { refactorNameInString, refactorNameInValue, ScopedDataTree } from './refactor.js'; import { createTriggerStepRenamer } from './rename.js'; import { Extractor } from './types.js'; export type PropertyUpdate = { propertyName: string; propertyValue: unknown; entityId: string }; const PAGE_WIDGET_ID = '0'; export async function getUpdatesInWidgets>({ widgets, oldName, newName, dataTree, extractPairs, namespace }: { widgets: T; oldName: string; newName: string; dataTree: ScopedDataTree; extractPairs: Extractor; namespace?: string; }): Promise { let updates: PropertyUpdate[] = []; for (const [widgetId, widget] of Object.entries(widgets)) { if (widget.widgetName === oldName) { updates.push({ entityId: widgetId, propertyName: 'widgetName', propertyValue: newName }); // if it's a table, rename in all of the cached columns (which aren't dynamic) if (widget.type === 'TABLE_WIDGET') { const tableWidget = widget as typeof widget & { cachedColumnSettings: Record> }; const cachedSettings = Object.keys(tableWidget.cachedColumnSettings ?? {}).reduce((accum, key) => { Object.entries(tableWidget.cachedColumnSettings[key]).forEach(([k, v]) => { if (typeof v === 'string') { accum.push(`cachedColumnSettings.${key}.${k}`); } }); return accum; }, [] as string[]); await Promise.all( cachedSettings.map(async (key: string) => { const binding = get(widget, key) as string; const withNewName = await refactorNameInString({ value: binding, oldName, newName, dataTree, namespace, extractPairs }); if (binding !== withNewName) { updates.push({ entityId: widgetId, propertyName: key, propertyValue: withNewName }); } }) ); } } if (widget.dynamicBindingPathList) { await Promise.all( widget.dynamicBindingPathList.map(async ({ key }) => { const binding = get(widget, key); const withNewName = await refactorNameInValue({ oldValue: binding, oldName, newName, dataTree, namespace, extractPairs }); if (!isEqual(binding, withNewName)) { updates.push({ entityId: widgetId, propertyName: key, propertyValue: withNewName }); } }) ); } if (widget.dynamicTriggerPathList) { const renameInTriggerStep = createTriggerStepRenamer({ oldName, newName, dataTree, addUpdate: (payload) => { updates.push({ ...payload, entityId: widgetId }); }, extractEvaluationPairs: extractPairs, namespace }); await Promise.all( widget.dynamicTriggerPathList.map(async ({ key }) => { const trigger: StepDef | undefined = get(widget, key) as StepDef | undefined; if (!isArray(trigger)) { return; } await Promise.all( trigger.map(async (step: StepDef, index) => { if (!isValidStepDef(step)) return; await renameInTriggerStep(step, key, index); }) ); }) ); } // Canvas widgets also contain API response info if (widgetId === PAGE_WIDGET_ID && 'apis' in widget) { const apiUpdates = await updateNameInEntityMap({ entityMap: widget.apis as PageDSL['apis'], parentId: widgetId, parentKey: 'apis', oldName, newName, dataTree, extractPairs, namespace }); updates = [...updates, ...apiUpdates]; } } return updates; } export async function updateNameInEntityMap({ entityMap, parentKey, parentId = '', oldName, newName, dataTree, extractPairs, namespace }: { entityMap: PageDSL['events'] | PageDSL['apis'] | PageDSL['timers'] | PageDSL['stateVars'] | undefined; parentId?: string; parentKey: string; oldName: string; newName: string; dataTree: ScopedDataTree; extractPairs: Extractor; namespace?: string; }): Promise { if (!entityMap) return []; const updates: PropertyUpdate[] = []; const renameInTriggerStep = createTriggerStepRenamer({ oldName, newName, dataTree, addUpdate: (payload) => { updates.push({ ...payload, entityId: parentId }); }, extractEvaluationPairs: extractPairs, namespace }); if ('timerMap' in entityMap) { const timers = entityMap.timerMap; await Promise.all( Object.values(timers).map(async (timer) => { if (timer.name === oldName) { updates.push({ propertyName: `${parentKey}.timerMap.${timer.id}.name`, propertyValue: newName, entityId: timer.id }); } await Promise.all( timer?.steps?.map(async (step, index) => { if (!isValidStepDef(step)) return; const mappedKey = `${parentKey}.timerMap.${timer.id}.steps`; await renameInTriggerStep(step, mappedKey, index); }) ); }) ); } else if ('apiMap' in entityMap) { const apis = entityMap.apiMap; for (const api of Object.values(apis)) { await Promise.all( (api.dynamicTriggerPathList ?? [])?.map(async ({ key }) => { const mappedKey = `${parentKey}.apiMap.${api.id}.${key}`; const triggers = get(api, key, []) as ValidMultiStepDef; await Promise.all( triggers.map(async (step: StepDef, index) => { if (!isValidStepDef(step)) return; await renameInTriggerStep(step, mappedKey, index); }) ); }) ); } } else if ('stateVarMap' in entityMap) { const stateVars = entityMap.stateVarMap; for (const stateVar of Object.values(stateVars)) { if (stateVar.name === oldName) { updates.push({ propertyName: `${parentKey}.stateVarMap.${stateVar.id}.name`, propertyValue: newName, entityId: stateVar.id }); } if (stateVar.dynamicBindingPathList) { await Promise.all( stateVar.dynamicBindingPathList.map(async ({ key }) => { const value = get(stateVar, key); const newValue = await refactorNameInValue({ oldValue: value, oldName, newName, dataTree, namespace, extractPairs }); if (!isEqual(value, newValue)) { updates.push({ entityId: parentId ?? stateVar.id, propertyName: `${parentKey}.stateVarMap.${stateVar.id}.defaultValue`, propertyValue: newValue }); } }) ); } } } return updates; }