Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cypress/e2e/13-pinning.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions cypress/pages/ndv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
10 changes: 8 additions & 2 deletions packages/editor-ui/src/components/RunData.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
24 changes: 17 additions & 7 deletions packages/editor-ui/src/composables/usePinnedData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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'),
);

Expand Down
9 changes: 5 additions & 4 deletions packages/editor-ui/src/plugins/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions packages/editor-ui/src/utils/typesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading