mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
fix(editor): Improve error messages around pinned data (#9632)
This commit is contained in:
parent
37531cdb7d
commit
a8bb53f4e3
|
@ -144,6 +144,19 @@ describe('Data pinning', () => {
|
||||||
.should('contain', 'Workflow has reached the maximum allowed pinned data size');
|
.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', () => {
|
it('Should be able to reference paired items in a node located before pinned data', () => {
|
||||||
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
workflowPage.actions.addInitialNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
|
||||||
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true);
|
workflowPage.actions.addNodeToCanvas(HTTP_REQUEST_NODE_NAME, true, true);
|
||||||
|
|
|
@ -151,14 +151,15 @@ export class NDV extends BasePage {
|
||||||
cy.contains('Expression').invoke('show').click();
|
cy.contains('Expression').invoke('show').click();
|
||||||
this.getters.inlineExpressionEditorInput().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.editPinnedDataButton().click();
|
||||||
|
|
||||||
this.getters.pinnedDataEditor().click();
|
this.getters.pinnedDataEditor().click();
|
||||||
this.getters
|
this.getters
|
||||||
.pinnedDataEditor()
|
.pinnedDataEditor()
|
||||||
.type(
|
.type(
|
||||||
`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`,
|
`{selectall}{backspace}${pinnedData.replace(new RegExp('{', 'g'), '{{}')}`,
|
||||||
{
|
{
|
||||||
delay: 0,
|
delay: 0,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1294,9 +1294,15 @@ export default defineComponent({
|
||||||
this.clearAllStickyNotifications();
|
this.clearAllStickyNotifications();
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
console.error(error);
|
// setData function already shows toasts on error, so just return here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showError(error, this.$locale.baseText('ndv.pinData.error.syntaxError.title'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
MAX_WORKFLOW_SIZE,
|
MAX_WORKFLOW_SIZE,
|
||||||
PIN_DATA_NODE_TYPES_DENYLIST,
|
PIN_DATA_NODE_TYPES_DENYLIST,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
import { stringSizeInBytes } from '@/utils/typesUtils';
|
import { stringSizeInBytes, toMegaBytes } from '@/utils/typesUtils';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import type { INodeUi, IRunDataDisplayMode } from '@/Interface';
|
import type { INodeUi, IRunDataDisplayMode } from '@/Interface';
|
||||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
|
@ -158,19 +158,29 @@ export function usePinnedData(
|
||||||
|
|
||||||
if (newPinDataSize > MAX_PINNED_DATA_SIZE) {
|
if (newPinDataSize > MAX_PINNED_DATA_SIZE) {
|
||||||
toast.showError(
|
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'),
|
i18n.baseText('ndv.pinData.error.tooLarge.title'),
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const workflowSize = stringSizeInBytes(workflowJson) + newPinDataSize;
|
||||||
stringSizeInBytes(workflowJson) + newPinDataSize >
|
const limit = MAX_WORKFLOW_SIZE - MAX_EXPECTED_REQUEST_SIZE;
|
||||||
MAX_WORKFLOW_SIZE - MAX_EXPECTED_REQUEST_SIZE
|
if (workflowSize > limit) {
|
||||||
) {
|
|
||||||
toast.showError(
|
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'),
|
i18n.baseText('ndv.pinData.error.tooLargeWorkflow.title'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -945,10 +945,11 @@
|
||||||
"ndv.pinData.beforeClosing.title": "Save output changes before closing?",
|
"ndv.pinData.beforeClosing.title": "Save output changes before closing?",
|
||||||
"ndv.pinData.beforeClosing.cancel": "Discard",
|
"ndv.pinData.beforeClosing.cancel": "Discard",
|
||||||
"ndv.pinData.beforeClosing.confirm": "Save",
|
"ndv.pinData.beforeClosing.confirm": "Save",
|
||||||
"ndv.pinData.error.tooLarge.title": "Pinned data too big",
|
"ndv.pinData.error.syntaxError.title": "Unable to save due to invalid JSON",
|
||||||
"ndv.pinData.error.tooLarge.description": "Workflow has reached the maximum allowed pinned data size",
|
"ndv.pinData.error.tooLarge.title": "Unable to pin data due to size limit",
|
||||||
"ndv.pinData.error.tooLargeWorkflow.title": "Pinned data too big",
|
"ndv.pinData.error.tooLarge.description": "Workflow has reached the maximum allowed pinned data size ({size} mb / {limit} mb)",
|
||||||
"ndv.pinData.error.tooLargeWorkflow.description": "Workflow has reached the maximum allowed size",
|
"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.",
|
"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.readyToOrganizeYourWorkflows": "Ready to organize your workflows?",
|
||||||
"noTagsView.withWorkflowTagsYouReFree": "With workflow tags, you're free to create the perfect tagging system for your flows",
|
"noTagsView.withWorkflowTagsYouReFree": "With workflow tags, you're free to create the perfect tagging system for your flows",
|
||||||
|
|
|
@ -63,6 +63,11 @@ export function stringSizeInBytes(input: string | IDataObject | IDataObject[] |
|
||||||
return new Blob([typeof input === 'string' ? input : JSON.stringify(input)]).size;
|
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) {
|
export function shorten(s: string, limit: number, keep: number) {
|
||||||
if (s.length <= limit) {
|
if (s.length <= limit) {
|
||||||
return s;
|
return s;
|
||||||
|
|
Loading…
Reference in a new issue