1
1
mirror of https://github.com/n8n-io/n8n.git synced 2024-09-11 13:15:28 +03:00

fix(editor): Improve error messages around pinned data (#9632)

This commit is contained in:
Milorad FIlipović 2024-06-05 13:48:55 +02:00 committed by GitHub
parent 37531cdb7d
commit a8bb53f4e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 51 additions and 15 deletions

View File

@ -144,6 +144,19 @@ describe('Data pinning', () => {
.should('contain', 'Workflow has reached the maximum allowed pinned data size');
});
it('Should show an error when pin data JSON in invalid', () => {
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
workflowPage.actions.addNodeToCanvas(EDIT_FIELDS_SET_NODE_NAME, true, true);
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
ndv.getters.editPinnedDataButton().should('be.visible');
ndv.actions.setPinnedData('[ { "name": "First item", "code": 2dsa }]')
workflowPage.getters
.errorToast()
.should('contain', 'Unable to save due to invalid JSON');
});
it('Should be able to reference paired items in a node located before pinned data', () => {
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true);

View File

@ -151,14 +151,15 @@ export class NDV extends BasePage {
cy.contains('Expression').invoke('show').click();
this.getters.inlineExpressionEditorInput().click();
},
setPinnedData: (data: object) => {
setPinnedData: (data: object | string) => {
const pinnedData = typeof data === 'string' ? data : JSON.stringify(data);
this.getters.editPinnedDataButton().click();
this.getters.pinnedDataEditor().click();
this.getters
.pinnedDataEditor()
.type(
`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`,
`{selectall}{backspace}${pinnedData.replace(new RegExp('{', 'g'), '{{}')}`,
{
delay: 0,
},

View File

@ -1294,9 +1294,15 @@ export default defineComponent({
this.clearAllStickyNotifications();
try {
this.pinnedData.setData(clearJsonKey(value) as INodeExecutionData[], 'save-edit');
const clearedValue = clearJsonKey(value) as INodeExecutionData[];
try {
this.pinnedData.setData(clearedValue, 'save-edit');
} catch (error) {
// setData function already shows toasts on error, so just return here
return;
}
} catch (error) {
console.error(error);
this.showError(error, this.$locale.baseText('ndv.pinData.error.syntaxError.title'));
return;
}

View File

@ -8,7 +8,7 @@ import {
MAX_WORKFLOW_SIZE,
PIN_DATA_NODE_TYPES_DENYLIST,
} from '@/constants';
import { stringSizeInBytes } from '@/utils/typesUtils';
import { stringSizeInBytes, toMegaBytes } from '@/utils/typesUtils';
import { useWorkflowsStore } from '@/stores/workflows.store';
import type { INodeUi, IRunDataDisplayMode } from '@/Interface';
import { useExternalHooks } from '@/composables/useExternalHooks';
@ -158,19 +158,29 @@ export function usePinnedData(
if (newPinDataSize > MAX_PINNED_DATA_SIZE) {
toast.showError(
new Error(i18n.baseText('ndv.pinData.error.tooLarge.description')),
new Error(
i18n.baseText('ndv.pinData.error.tooLarge.description', {
interpolate: {
size: toMegaBytes(newPinDataSize),
limit: toMegaBytes(MAX_PINNED_DATA_SIZE),
},
}),
),
i18n.baseText('ndv.pinData.error.tooLarge.title'),
);
return false;
}
if (
stringSizeInBytes(workflowJson) + newPinDataSize >
MAX_WORKFLOW_SIZE - MAX_EXPECTED_REQUEST_SIZE
) {
const workflowSize = stringSizeInBytes(workflowJson) + newPinDataSize;
const limit = MAX_WORKFLOW_SIZE - MAX_EXPECTED_REQUEST_SIZE;
if (workflowSize > limit) {
toast.showError(
new Error(i18n.baseText('ndv.pinData.error.tooLargeWorkflow.description')),
new Error(
i18n.baseText('ndv.pinData.error.tooLargeWorkflow.description', {
interpolate: { size: toMegaBytes(workflowSize), limit: toMegaBytes(limit) },
}),
),
i18n.baseText('ndv.pinData.error.tooLargeWorkflow.title'),
);

View File

@ -945,10 +945,11 @@
"ndv.pinData.beforeClosing.title": "Save output changes before closing?",
"ndv.pinData.beforeClosing.cancel": "Discard",
"ndv.pinData.beforeClosing.confirm": "Save",
"ndv.pinData.error.tooLarge.title": "Pinned data too big",
"ndv.pinData.error.tooLarge.description": "Workflow has reached the maximum allowed pinned data size",
"ndv.pinData.error.tooLargeWorkflow.title": "Pinned data too big",
"ndv.pinData.error.tooLargeWorkflow.description": "Workflow has reached the maximum allowed size",
"ndv.pinData.error.syntaxError.title": "Unable to save due to invalid JSON",
"ndv.pinData.error.tooLarge.title": "Unable to pin data due to size limit",
"ndv.pinData.error.tooLarge.description": "Workflow has reached the maximum allowed pinned data size ({size} mb / {limit} mb)",
"ndv.pinData.error.tooLargeWorkflow.title": "Unable to pin data due to size limit",
"ndv.pinData.error.tooLargeWorkflow.description": "Workflow has reached the maximum allowed size ({size} mb / {limit} mb)",
"ndv.httpRequest.credentialOnly.docsNotice": "Use the <a target=\"_blank\" href=\"{docsUrl}\">{nodeName} docs</a> to construct your request. We'll take care of the authentication part if you add a {nodeName} credential below.",
"noTagsView.readyToOrganizeYourWorkflows": "Ready to organize your workflows?",
"noTagsView.withWorkflowTagsYouReFree": "With workflow tags, you're free to create the perfect tagging system for your flows",

View File

@ -63,6 +63,11 @@ export function stringSizeInBytes(input: string | IDataObject | IDataObject[] |
return new Blob([typeof input === 'string' ? input : JSON.stringify(input)]).size;
}
export function toMegaBytes(bytes: number, decimalPlaces: number = 2): number {
const megabytes = bytes / 1024 / 1024;
return parseFloat(megabytes.toFixed(decimalPlaces));
}
export function shorten(s: string, limit: number, keep: number) {
if (s.length <= limit) {
return s;