mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Fix: handle error gracefully for failed saving changes (#512)
* handle error gracefully for event handlers attachment do not exist * handle error on saving changes - added server side validation checks - client side error message - removed autoSave triggers from priotity queue
This commit is contained in:
parent
67cb1455fa
commit
8805af9ce1
5 changed files with 108 additions and 33 deletions
|
|
@ -82,7 +82,12 @@ import { HotkeysProvider } from 'react-hotkeys-hook';
|
|||
import { useResolveStore } from '@/_stores/resolverStore';
|
||||
import { dfs } from '@/_stores/handleReferenceTransactions';
|
||||
import { decimalToHex, EditorConstants } from './editorConstants';
|
||||
import { handleLowPriorityWork, updateCanvasBackground, clearAllQueuedTasks } from '@/_helpers/editorHelpers';
|
||||
import {
|
||||
handleLowPriorityWork,
|
||||
updateCanvasBackground,
|
||||
clearAllQueuedTasks,
|
||||
checkAndExtractEntityId,
|
||||
} from '@/_helpers/editorHelpers';
|
||||
import { TJLoader } from '@/_ui/TJLoader/TJLoader';
|
||||
import cx from 'classnames';
|
||||
import { resolveReferences } from './CodeEditor/utils';
|
||||
|
|
@ -286,7 +291,7 @@ const EditorComponent = (props) => {
|
|||
|
||||
if (appDiffOptions?.skipAutoSave === true || appDiffOptions?.entityReferenceUpdated === true) return;
|
||||
|
||||
handleLowPriorityWork(() => autoSave(), 100);
|
||||
autoSave();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify({ appDefinition, currentPageId, dataQueries })]);
|
||||
|
|
@ -1038,26 +1043,24 @@ const EditorComponent = (props) => {
|
|||
}
|
||||
|
||||
//Todo: Move this to a separate function or as a middleware of the api to createing a component
|
||||
handleLowPriorityWork(() => {
|
||||
if (updateDiff?.type === 'components' && updateDiff?.operation === 'create') {
|
||||
const componentsFromCurrentState = getCurrentState().components;
|
||||
const newComponentIds = Object.keys(updateDiff?.updateDiff);
|
||||
const newComponentsExposedData = {};
|
||||
const componentEntityArray = [];
|
||||
Object.values(componentsFromCurrentState).filter((component) => {
|
||||
if (newComponentIds.includes(component.id)) {
|
||||
const componentName = updateDiff?.updateDiff[component.id]?.name;
|
||||
newComponentsExposedData[componentName] = component;
|
||||
componentEntityArray.push({ id: component.id, name: componentName });
|
||||
}
|
||||
});
|
||||
if (updateDiff?.type === 'components' && updateDiff?.operation === 'create') {
|
||||
const componentsFromCurrentState = getCurrentState().components;
|
||||
const newComponentIds = Object.keys(updateDiff?.updateDiff);
|
||||
const newComponentsExposedData = {};
|
||||
const componentEntityArray = [];
|
||||
Object.values(componentsFromCurrentState).filter((component) => {
|
||||
if (newComponentIds.includes(component.id)) {
|
||||
const componentName = updateDiff?.updateDiff[component.id]?.name;
|
||||
newComponentsExposedData[componentName] = component;
|
||||
componentEntityArray.push({ id: component.id, name: componentName });
|
||||
}
|
||||
});
|
||||
|
||||
useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
|
||||
useResolveStore.getState().actions.addAppSuggestions({
|
||||
components: newComponentsExposedData,
|
||||
});
|
||||
}
|
||||
});
|
||||
useResolveStore.getState().actions.addEntitiesToMap(componentEntityArray);
|
||||
useResolveStore.getState().actions.addAppSuggestions({
|
||||
components: newComponentsExposedData,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
updateDiff?.type === 'components' &&
|
||||
|
|
@ -1080,13 +1083,24 @@ const EditorComponent = (props) => {
|
|||
isUpdatingEditorStateInProcess: false,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch((e) => {
|
||||
const entityNotSaved =
|
||||
e?.data?.statusCode === 500 && e?.error
|
||||
? checkAndExtractEntityId(e.error)
|
||||
: { entityId: null, message: 'App could not be saved.' };
|
||||
|
||||
let errMessage = e?.data?.message || 'App could not be saved.';
|
||||
if (entityNotSaved.entityId) {
|
||||
const componentName =
|
||||
appDefinition.pages[currentPageId].components[entityNotSaved.entityId]?.component?.name;
|
||||
errMessage = `The component "${componentName}" could not be saved, so the last action is also not saved.`;
|
||||
}
|
||||
|
||||
updateEditorState({
|
||||
saveError: true,
|
||||
isUpdatingEditorStateInProcess: false,
|
||||
});
|
||||
// toast.error('App could not save.');
|
||||
toast.error(err?.error ?? 'App could not save.');
|
||||
toast.error(errMessage);
|
||||
})
|
||||
.finally(() => {
|
||||
if (appDiffOptions?.cloningComponent) {
|
||||
|
|
|
|||
|
|
@ -336,3 +336,18 @@ export const updateCanvasBackground = ({ canvasBackgroundColor, backgroundFxQuer
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function checkAndExtractEntityId(errorString) {
|
||||
const regex = /"([a-f0-9-]+)"/;
|
||||
const match = errorString.match(regex);
|
||||
if (match && match[1]) {
|
||||
return {
|
||||
entityId: match[1],
|
||||
message: 'The last component is not saved, so the last action is also not saved.',
|
||||
};
|
||||
}
|
||||
return {
|
||||
entityId: null,
|
||||
message: 'No entity ID found in the error message.',
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { useDataQueriesStore } from './dataQueriesStore';
|
|||
import _ from 'lodash';
|
||||
import { dfs, handleReferenceTransactions } from './handleReferenceTransactions';
|
||||
import { isValidUUID } from '@/_helpers/utils';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
const initialState = {
|
||||
editingVersion: null,
|
||||
|
|
@ -137,13 +138,22 @@ export const useAppDataStore = create(
|
|||
const versionId = get().currentVersionId;
|
||||
|
||||
const updatedEvents = get().events;
|
||||
const response = await appVersionService.createAppVersionEventHandler(appId, versionId, event);
|
||||
get().actions.setIsSaving(false);
|
||||
set({ eventsCreatedLoader: false });
|
||||
appVersionService
|
||||
.createAppVersionEventHandler(appId, versionId, event)
|
||||
.then((response) => {
|
||||
get().actions.setIsSaving(false);
|
||||
set({ eventsCreatedLoader: false });
|
||||
|
||||
updatedEvents.push(response);
|
||||
updatedEvents.push(response);
|
||||
|
||||
set(() => ({ events: updatedEvents }));
|
||||
set(() => ({ events: updatedEvents }));
|
||||
})
|
||||
.catch((err) => {
|
||||
get().actions.setIsSaving(false);
|
||||
set({ eventsCreatedLoader: false });
|
||||
|
||||
toast.error(err?.error || 'An error occurred while creating the event handler');
|
||||
});
|
||||
},
|
||||
|
||||
deleteAppVersionEventHandler: async (eventId) => {
|
||||
|
|
|
|||
|
|
@ -72,9 +72,9 @@ export class ComponentsService {
|
|||
for (const componentId in componentDiff) {
|
||||
const { component } = componentDiff[componentId];
|
||||
|
||||
const componentData: Component = await manager.findOne(Component, componentId);
|
||||
const doesComponentExist = await manager.findOneOrFail(Component, componentId);
|
||||
|
||||
if (!componentData) {
|
||||
if (doesComponentExist[1] === 0) {
|
||||
return {
|
||||
error: {
|
||||
message: `Component with id ${componentId} does not exist`,
|
||||
|
|
@ -82,6 +82,8 @@ export class ComponentsService {
|
|||
};
|
||||
}
|
||||
|
||||
const componentData: Component = await manager.findOne(Component, componentId);
|
||||
|
||||
const isComponentDefinitionChanged = component.definition ? true : false;
|
||||
|
||||
if (isComponentDefinitionChanged) {
|
||||
|
|
@ -151,9 +153,9 @@ export class ComponentsService {
|
|||
) {
|
||||
return dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => {
|
||||
for (const componentId in componenstLayoutDiff) {
|
||||
const doesComponentExist = await manager.findAndCount(Component, { id: componentId });
|
||||
const doesComponentExist = await manager.findOneOrFail(Component, componentId);
|
||||
|
||||
if (!doesComponentExist[1]) {
|
||||
if (doesComponentExist[1] === 0) {
|
||||
return {
|
||||
error: {
|
||||
message: `Component with id ${componentId} does not exist`,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,40 @@ export class EventsService {
|
|||
}
|
||||
|
||||
return await dbTransactionForAppVersionAssociationsUpdate(async (manager: EntityManager) => {
|
||||
if (
|
||||
eventHandler.eventType === 'component' ||
|
||||
eventHandler.eventType === 'table_column' ||
|
||||
eventHandler.eventType === 'table_action'
|
||||
) {
|
||||
const componentExists = await manager.findOne('Component', {
|
||||
id: eventHandler.attachedTo,
|
||||
});
|
||||
|
||||
if (!componentExists) {
|
||||
throw new BadRequestException('Component does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
if (eventHandler.eventType === 'data_query') {
|
||||
const dataQueryExists = await manager.findOne('DataQuery', {
|
||||
id: eventHandler.attachedTo,
|
||||
});
|
||||
|
||||
if (!dataQueryExists) {
|
||||
throw new BadRequestException('Data Query does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
if (eventHandler.eventType === 'page') {
|
||||
const pageExists = await manager.findOne('Page', {
|
||||
id: eventHandler.attachedTo,
|
||||
});
|
||||
|
||||
if (!pageExists) {
|
||||
throw new BadRequestException('Page does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
const newEvent = new EventHandler();
|
||||
newEvent.name = eventHandler.event.eventId;
|
||||
newEvent.sourceId = eventHandler.attachedTo;
|
||||
|
|
|
|||
Loading…
Reference in a new issue