feat: Replace all Vue.set usages with direct assignment and spread operator (no-changelog) (#6280)

* refactor: replace all Vue.set usages with direct assignment and spread operator

* chore: fix linting issue

* fix: fix updateNodeAtIndex function

* fix: various post-refactoring fixes

* fix: refactor recently added Vue.set directive
This commit is contained in:
Alex Grozav
2023-06-15 15:30:05 +03:00
committed by GitHub
parent c2afed4ca1
commit 596cf07e42
24 changed files with 620 additions and 307 deletions

View File

@@ -29,7 +29,6 @@ describe('Canvas Node Manipulation and Navigation', () => {
WorkflowPage.actions.visit(); WorkflowPage.actions.visit();
}); });
it('should add switch node and test connections', () => { it('should add switch node and test connections', () => {
WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true); WorkflowPage.actions.addNodeToCanvas(SWITCH_NODE_NAME, true);
@@ -114,7 +113,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
WorkflowPage.actions.zoomToFit(); WorkflowPage.actions.zoomToFit();
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success'); cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
cy.get('.jtk-connector.success').should('have.length', 3); cy.get('.jtk-connector.success').should('have.length', 4);
cy.get('.jtk-connector').should('have.length', 4); cy.get('.jtk-connector').should('have.length', 4);
}); });

View File

@@ -16,12 +16,10 @@ describe('Data mapping', () => {
beforeEach(() => { beforeEach(() => {
workflowPage.actions.visit(); workflowPage.actions.visit();
cy.window().then( cy.window().then((win) => {
(win) => {
// @ts-ignore // @ts-ignore
win.preventNodeViewBeforeUnload = true; win.preventNodeViewBeforeUnload = true;
}, });
);
}); });
it('maps expressions from table header', () => { it('maps expressions from table header', () => {
@@ -303,19 +301,28 @@ describe('Data mapping', () => {
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('exist'); ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('exist');
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]').should('not.exist'); ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]').should('not.exist');
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown().realMouseMove(100, 100); ndv.getters
.inputDataContainer()
.should('exist')
.find('span')
.contains('count')
.realMouseDown()
.realMouseMove(100, 100);
cy.wait(50); cy.wait(50);
ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('not.exist'); ndv.getters.parameterInput('keepOnlySet').find('input[type="checkbox"]').should('not.exist');
ndv.getters.parameterInput('keepOnlySet').find('input[type="text"]') ndv.getters
.parameterInput('keepOnlySet')
.find('input[type="text"]')
.should('exist') .should('exist')
.invoke('css', 'border') .invoke('css', 'border')
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)')); .then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
ndv.getters.parameterInput('value').find('input[type="text"]') ndv.getters
.parameterInput('value')
.find('input[type="text"]')
.should('exist') .should('exist')
.invoke('css', 'border') .invoke('css', 'border')
.then((border) => expect(border).to.include('dashed rgb(90, 76, 194)')); .then((border) => expect(border).to.include('dashed rgb(90, 76, 194)'));
}); });
}); });

View File

@@ -13,14 +13,17 @@ export class NDV extends BasePage {
outputPanel: () => cy.getByTestId('output-panel'), outputPanel: () => cy.getByTestId('output-panel'),
executingLoader: () => cy.getByTestId('ndv-executing'), executingLoader: () => cy.getByTestId('ndv-executing'),
inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'), inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'),
inputDisplayMode: () => this.getters.inputPanel().findChildByTestId('ndv-run-data-display-mode').first(), inputDisplayMode: () =>
this.getters.inputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'), outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'),
outputDisplayMode: () => this.getters.outputPanel().findChildByTestId('ndv-run-data-display-mode').first(), outputDisplayMode: () =>
this.getters.outputPanel().findChildByTestId('ndv-run-data-display-mode').first(),
pinDataButton: () => cy.getByTestId('ndv-pin-data'), pinDataButton: () => cy.getByTestId('ndv-pin-data'),
editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'), editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'),
pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller'), pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller'),
runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'), runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'), savePinnedDataButton: () =>
this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'),
outputTableRows: () => this.getters.outputDataContainer().find('table tr'), outputTableRows: () => this.getters.outputDataContainer().find('table tr'),
outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'), outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'),
outputTableRow: (row: number) => this.getters.outputTableRows().eq(row), outputTableRow: (row: number) => this.getters.outputTableRows().eq(row),
@@ -52,10 +55,13 @@ export class NDV extends BasePage {
outputBranches: () => this.getters.outputPanel().findChildByTestId('branches'), outputBranches: () => this.getters.outputPanel().findChildByTestId('branches'),
inputBranches: () => this.getters.inputPanel().findChildByTestId('branches'), inputBranches: () => this.getters.inputPanel().findChildByTestId('branches'),
resourceLocator: (paramName: string) => cy.getByTestId(`resource-locator-${paramName}`), resourceLocator: (paramName: string) => cy.getByTestId(`resource-locator-${paramName}`),
resourceLocatorInput: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="rlc-input-container"]'), resourceLocatorInput: (paramName: string) =>
resourceLocatorDropdown: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="resource-locator-dropdown"]'), this.getters.resourceLocator(paramName).find('[data-test-id="rlc-input-container"]'),
resourceLocatorDropdown: (paramName: string) =>
this.getters.resourceLocator(paramName).find('[data-test-id="resource-locator-dropdown"]'),
resourceLocatorErrorMessage: () => cy.getByTestId('rlc-error-container'), resourceLocatorErrorMessage: () => cy.getByTestId('rlc-error-container'),
resourceLocatorModeSelector: (paramName: string) => this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'), resourceLocatorModeSelector: (paramName: string) =>
this.getters.resourceLocator(paramName).find('[data-test-id="rlc-mode-selector"]'),
}; };
actions = { actions = {
@@ -82,7 +88,9 @@ export class NDV extends BasePage {
this.getters.editPinnedDataButton().click(); this.getters.editPinnedDataButton().click();
this.getters.pinnedDataEditor().click(); this.getters.pinnedDataEditor().click();
this.getters.pinnedDataEditor().type(`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`); this.getters
.pinnedDataEditor()
.type(`{selectall}{backspace}${JSON.stringify(data).replace(new RegExp('{', 'g'), '{{}')}`);
this.actions.savePinnedData(); this.actions.savePinnedData();
}, },
@@ -131,15 +139,11 @@ export class NDV extends BasePage {
}, },
changeInputRunSelector: (runName: string) => { changeInputRunSelector: (runName: string) => {
this.getters.inputRunSelector().click(); this.getters.inputRunSelector().click();
cy.get('.el-select-dropdown:visible .el-select-dropdown__item') cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
.contains(runName)
.click();
}, },
changeOutputRunSelector: (runName: string) => { changeOutputRunSelector: (runName: string) => {
this.getters.outputRunSelector().click(); this.getters.outputRunSelector().click();
cy.get('.el-select-dropdown:visible .el-select-dropdown__item') cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
.contains(runName)
.click();
}, },
toggleOutputRunLinking: () => { toggleOutputRunLinking: () => {
this.getters.outputRunSelector().find('button').click(); this.getters.outputRunSelector().find('button').click();
@@ -159,7 +163,10 @@ export class NDV extends BasePage {
this.getters.resourceLocatorInput(paramName).type(value); this.getters.resourceLocatorInput(paramName).type(value);
}, },
validateExpressionPreview: (paramName: string, value: string) => { validateExpressionPreview: (paramName: string, value: string) => {
this.getters.parameterExpressionPreview(paramName).find('span').should('include.html', asEncodedHTML(value)); this.getters
.parameterExpressionPreview(paramName)
.find('span')
.should('include.html', asEncodedHTML(value));
}, },
}; };
} }
@@ -172,4 +179,3 @@ function asEncodedHTML(str: string): string {
.replace(/"/g, '"') .replace(/"/g, '"')
.replace(/ /g, ' '); .replace(/ /g, ' ');
} }

View File

@@ -975,13 +975,10 @@ export interface ITagsState {
fetchedUsageCount: boolean; fetchedUsageCount: boolean;
} }
export type Modals = export type Modals = {
| {
[key: string]: ModalState;
}
| {
[CREDENTIAL_EDIT_MODAL_KEY]: NewCredentialsModal; [CREDENTIAL_EDIT_MODAL_KEY]: NewCredentialsModal;
}; [key: string]: ModalState;
};
export type ModalState = { export type ModalState = {
open: boolean; open: boolean;

View File

@@ -11,7 +11,6 @@ import {
} from '@/utils'; } from '@/utils';
import type { INodeProperties, INodeTypeDescription, NodeParameterValue } from 'n8n-workflow'; import type { INodeProperties, INodeTypeDescription, NodeParameterValue } from 'n8n-workflow';
import { computed, onMounted, ref } from 'vue'; import { computed, onMounted, ref } from 'vue';
import Vue from 'vue';
export interface Props { export interface Props {
credentialType: Object; credentialType: Object;
@@ -27,7 +26,7 @@ const ndvStore = useNDVStore();
const props = defineProps<Props>(); const props = defineProps<Props>();
const selected = ref(''); const selected = ref('');
const authRelatedFieldsValues = ref({} as { [key: string]: NodeParameterValue }); const authRelatedFieldsValues = ref<{ [key: string]: NodeParameterValue }>({});
onMounted(() => { onMounted(() => {
if (activeNodeType.value?.credentials) { if (activeNodeType.value?.credentials) {
@@ -43,7 +42,10 @@ onMounted(() => {
// Populate default values of related fields // Populate default values of related fields
authRelatedFields.value.forEach((field) => { authRelatedFields.value.forEach((field) => {
Vue.set(authRelatedFieldsValues.value, field.name, field.default); authRelatedFieldsValues.value = {
...authRelatedFieldsValues.value,
[field.name]: field.default as NodeParameterValue,
};
}); });
}); });
@@ -102,7 +104,10 @@ function onAuthTypeChange(newType: string): void {
} }
function valueChanged(data: IUpdateInformation): void { function valueChanged(data: IUpdateInformation): void {
Vue.set(authRelatedFieldsValues.value, data.name, data.value); authRelatedFieldsValues.value = {
...authRelatedFieldsValues.value,
[data.name]: data.value as NodeParameterValue,
};
} }
defineExpose({ defineExpose({

View File

@@ -264,7 +264,7 @@ export default defineComponent({
); );
}, },
credentialTypeName(): string { credentialTypeName(): string {
return (this.credentialType as ICredentialType).name; return (this.credentialType as ICredentialType)?.name;
}, },
credentialOwnerName(): string { credentialOwnerName(): string {
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`); return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);

View File

@@ -109,7 +109,6 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
@@ -234,12 +233,15 @@ export default defineComponent({
}); });
if (this.currentUser) { if (this.currentUser) {
Vue.set(this.credentialData, 'ownedBy', { this.credentialData = {
...this.credentialData,
ownedBy: {
id: this.currentUser.id, id: this.currentUser.id,
firstName: this.currentUser.firstName, firstName: this.currentUser.firstName,
lastName: this.currentUser.lastName, lastName: this.currentUser.lastName,
email: this.currentUser.email, email: this.currentUser.email,
}); },
};
} }
} else { } else {
await this.loadCurrentCredential(); await this.loadCurrentCredential();
@@ -251,7 +253,10 @@ export default defineComponent({
!this.credentialData.hasOwnProperty(property.name) && !this.credentialData.hasOwnProperty(property.name) &&
!this.credentialType.__overwrittenProperties?.includes(property.name) !this.credentialType.__overwrittenProperties?.includes(property.name)
) { ) {
Vue.set(this.credentialData, property.name, property.default as CredentialInformation); this.credentialData = {
...this.credentialData,
[property.name]: property.default as CredentialInformation,
};
} }
} }
} }
@@ -594,12 +599,18 @@ export default defineComponent({
); );
} }
this.credentialData = currentCredentials.data || {}; this.credentialData = (currentCredentials.data as ICredentialDataDecryptedObject) || {};
if (currentCredentials.sharedWith) { if (currentCredentials.sharedWith) {
Vue.set(this.credentialData, 'sharedWith', currentCredentials.sharedWith); this.credentialData = {
...this.credentialData,
sharedWith: currentCredentials.sharedWith as IDataObject[],
};
} }
if (currentCredentials.ownedBy) { if (currentCredentials.ownedBy) {
Vue.set(this.credentialData, 'ownedBy', currentCredentials.ownedBy); this.credentialData = {
...this.credentialData,
ownedBy: currentCredentials.ownedBy as IDataObject[],
};
} }
this.credentialName = currentCredentials.name; this.credentialName = currentCredentials.name;
@@ -650,7 +661,10 @@ export default defineComponent({
} }
}, },
onChangeSharedWith(sharees: IDataObject[]) { onChangeSharedWith(sharees: IDataObject[]) {
Vue.set(this.credentialData, 'sharedWith', sharees); this.credentialData = {
...this.credentialData,
sharedWith: sharees,
};
this.hasUnsavedChanges = true; this.hasUnsavedChanges = true;
}, },
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -997,7 +1011,11 @@ export default defineComponent({
const params = const params =
'scrollbars=no,resizable=yes,status=no,titlebar=noe,location=no,toolbar=no,menubar=no,width=500,height=700'; 'scrollbars=no,resizable=yes,status=no,titlebar=noe,location=no,toolbar=no,menubar=no,width=500,height=700';
const oauthPopup = window.open(url, 'OAuth2 Authorization', params); const oauthPopup = window.open(url, 'OAuth2 Authorization', params);
Vue.set(this.credentialData, 'oauthTokenData', null);
this.credentialData = {
...this.credentialData,
oauthTokenData: null as unknown as CredentialInformation,
};
const receiveMessage = (event: MessageEvent) => { const receiveMessage = (event: MessageEvent) => {
// // TODO: Add check that it came from n8n // // TODO: Add check that it came from n8n
@@ -1009,7 +1027,11 @@ export default defineComponent({
// Set some kind of data that status changes. // Set some kind of data that status changes.
// As data does not get displayed directly it does not matter what data. // As data does not get displayed directly it does not matter what data.
Vue.set(this.credentialData, 'oauthTokenData', {}); this.credentialData = {
...this.credentialData,
oauthTokenData: {} as CredentialInformation,
};
this.credentialsStore.enableOAuthCredential(credential); this.credentialsStore.enableOAuthCredential(credential);
// Close the window // Close the window
@@ -1061,7 +1083,10 @@ export default defineComponent({
} }
for (const property of this.credentialType.properties) { for (const property of this.credentialType.properties) {
if (!this.credentialType.__overwrittenProperties?.includes(property.name)) { if (!this.credentialType.__overwrittenProperties?.includes(property.name)) {
Vue.set(this.credentialData, property.name, property.default as CredentialInformation); this.credentialData = {
...this.credentialData,
[property.name]: property.default as CredentialInformation,
};
} }
} }
}, },

View File

@@ -432,7 +432,7 @@ export default defineComponent({
this.allVisibleSelected = !this.allVisibleSelected; this.allVisibleSelected = !this.allVisibleSelected;
if (!this.allVisibleSelected) { if (!this.allVisibleSelected) {
this.allExistingSelected = false; this.allExistingSelected = false;
Vue.set(this, 'selectedItems', {}); this.selectedItems = {};
} else { } else {
this.selectAllVisibleExecutions(); this.selectAllVisibleExecutions();
} }
@@ -441,7 +441,10 @@ export default defineComponent({
if (this.selectedItems[executionId]) { if (this.selectedItems[executionId]) {
Vue.delete(this.selectedItems, executionId); Vue.delete(this.selectedItems, executionId);
} else { } else {
Vue.set(this.selectedItems, executionId, true); this.selectedItems = {
...this.selectedItems,
[executionId]: true,
};
} }
this.allVisibleSelected = this.allVisibleSelected =
Object.keys(this.selectedItems).length === this.combinedExecutions.length; Object.keys(this.selectedItems).length === this.combinedExecutions.length;
@@ -502,7 +505,7 @@ export default defineComponent({
handleClearSelection(): void { handleClearSelection(): void {
this.allVisibleSelected = false; this.allVisibleSelected = false;
this.allExistingSelected = false; this.allExistingSelected = false;
Vue.set(this, 'selectedItems', {}); this.selectedItems = {};
}, },
async onFilterChanged(filter: ExecutionFilterType) { async onFilterChanged(filter: ExecutionFilterType) {
this.filter = filter; this.filter = filter;
@@ -635,7 +638,7 @@ export default defineComponent({
this.finishedExecutionsCount = pastExecutions.count; this.finishedExecutionsCount = pastExecutions.count;
this.finishedExecutionsCountEstimated = pastExecutions.estimated; this.finishedExecutionsCountEstimated = pastExecutions.estimated;
Vue.set(this, 'finishedExecutions', alreadyPresentExecutionsFiltered); this.finishedExecutions = alreadyPresentExecutionsFiltered;
this.workflowsStore.addToCurrentExecutions(alreadyPresentExecutionsFiltered); this.workflowsStore.addToCurrentExecutions(alreadyPresentExecutionsFiltered);
this.adjustSelectionAfterMoreItemsLoaded(); this.adjustSelectionAfterMoreItemsLoaded();
@@ -706,7 +709,8 @@ export default defineComponent({
}, },
async loadWorkflows() { async loadWorkflows() {
try { try {
const workflows = await this.workflowsStore.fetchAllWorkflows(); const workflows =
(await this.workflowsStore.fetchAllWorkflows()) as IWorkflowShortResponse[];
workflows.sort((a, b) => { workflows.sort((a, b) => {
if (a.name.toLowerCase() < b.name.toLowerCase()) { if (a.name.toLowerCase() < b.name.toLowerCase()) {
return -1; return -1;
@@ -717,13 +721,12 @@ export default defineComponent({
return 0; return 0;
}); });
// @ts-ignore
workflows.unshift({ workflows.unshift({
id: 'all', id: 'all',
name: this.$locale.baseText('executionsList.allWorkflows'), name: this.$locale.baseText('executionsList.allWorkflows'),
}); } as IWorkflowShortResponse);
Vue.set(this, 'workflows', workflows); this.workflows = workflows;
} catch (error) { } catch (error) {
this.showError( this.showError(
error, error,
@@ -900,7 +903,7 @@ export default defineComponent({
await this.refreshData(); await this.refreshData();
if (this.allVisibleSelected) { if (this.allVisibleSelected) {
Vue.set(this, 'selectedItems', {}); this.selectedItems = {};
this.selectAllVisibleExecutions(); this.selectAllVisibleExecutions();
} }
} catch (error) { } catch (error) {
@@ -922,7 +925,7 @@ export default defineComponent({
}, },
selectAllVisibleExecutions() { selectAllVisibleExecutions() {
this.combinedExecutions.forEach((execution: IExecutionsSummary) => { this.combinedExecutions.forEach((execution: IExecutionsSummary) => {
Vue.set(this.selectedItems, execution.id, true); this.selectedItems = { ...this.selectedItems, [execution.id]: true };
}); });
}, },
adjustSelectionAfterMoreItemsLoaded() { adjustSelectionAfterMoreItemsLoaded() {

View File

@@ -409,8 +409,8 @@ export default defineComponent({
}, },
}, },
watch: { watch: {
activeNode(node: INodeUi | null) { activeNode(node: INodeUi | null, oldNode: INodeUi | null) {
if (node && !this.isActiveStickyNode) { if (node && node.name !== oldNode?.name && !this.isActiveStickyNode) {
this.runInputIndex = -1; this.runInputIndex = -1;
this.runOutputIndex = -1; this.runOutputIndex = -1;
this.isLinkingEnabled = true; this.isLinkingEnabled = true;

View File

@@ -533,8 +533,10 @@ export default defineComponent({
Vue.delete(this.nodeValues, lastNamePart); Vue.delete(this.nodeValues, lastNamePart);
} else { } else {
// Value should be set // Value should be set
// @ts-ignore this.nodeValues = {
Vue.set(this.nodeValues, lastNamePart, value); ...this.nodeValues,
[lastNamePart as string]: value,
};
} }
} else { } else {
// Data is on lower level // Data is on lower level
@@ -556,14 +558,22 @@ export default defineComponent({
} else { } else {
// Value should be set // Value should be set
if (typeof value === 'object') { if (typeof value === 'object') {
// @ts-ignore set(
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, deepCopy(value)); get(this.nodeValues, nameParts.join('.')) as Record<string, unknown>,
lastNamePart as string,
deepCopy(value),
);
} else { } else {
// @ts-ignore set(
Vue.set(get(this.nodeValues, nameParts.join('.')), lastNamePart, value); get(this.nodeValues, nameParts.join('.')) as Record<string, unknown>,
lastNamePart as string,
value,
);
} }
} }
} }
this.nodeValues = { ...this.nodeValues };
}, },
credentialSelected(updateInformation: INodeUpdatePropertiesInformation) { credentialSelected(updateInformation: INodeUpdatePropertiesInformation) {
// Update the values on the node // Update the values on the node
@@ -660,7 +670,7 @@ export default defineComponent({
if (Array.isArray(data)) { if (Array.isArray(data)) {
data.splice(parseInt(index, 10), 1); data.splice(parseInt(index, 10), 1);
Vue.set(nodeParameters as object, path, data); set(nodeParameters as object, path, data);
} }
} else { } else {
if (newValue === undefined) { if (newValue === undefined) {
@@ -744,7 +754,7 @@ export default defineComponent({
if (Array.isArray(data)) { if (Array.isArray(data)) {
data.splice(parseInt(index, 10), 1); data.splice(parseInt(index, 10), 1);
Vue.set(nodeParameters as object, path, data); set(nodeParameters as object, path, data);
} }
} else { } else {
if (newValue === undefined) { if (newValue === undefined) {
@@ -791,7 +801,10 @@ export default defineComponent({
// A property on the node itself changed // A property on the node itself changed
// Update data in settings // Update data in settings
Vue.set(this.nodeValues, parameterData.name, newValue); this.nodeValues = {
...this.nodeValues,
[parameterData.name]: newValue,
};
// Update data in vuex // Update data in vuex
const updateInformation = { const updateInformation = {
@@ -818,58 +831,91 @@ export default defineComponent({
const foundNodeSettings = []; const foundNodeSettings = [];
if (this.node.color) { if (this.node.color) {
foundNodeSettings.push('color'); foundNodeSettings.push('color');
Vue.set(this.nodeValues, 'color', this.node.color); this.nodeValues = {
...this.nodeValues,
color: this.node.color,
};
} }
if (this.node.notes) { if (this.node.notes) {
foundNodeSettings.push('notes'); foundNodeSettings.push('notes');
Vue.set(this.nodeValues, 'notes', this.node.notes); this.nodeValues = {
...this.nodeValues,
notes: this.node.notes,
};
} }
if (this.node.alwaysOutputData) { if (this.node.alwaysOutputData) {
foundNodeSettings.push('alwaysOutputData'); foundNodeSettings.push('alwaysOutputData');
Vue.set(this.nodeValues, 'alwaysOutputData', this.node.alwaysOutputData); this.nodeValues = {
...this.nodeValues,
alwaysOutputData: this.node.alwaysOutputData,
};
} }
if (this.node.executeOnce) { if (this.node.executeOnce) {
foundNodeSettings.push('executeOnce'); foundNodeSettings.push('executeOnce');
Vue.set(this.nodeValues, 'executeOnce', this.node.executeOnce); this.nodeValues = {
...this.nodeValues,
executeOnce: this.node.executeOnce,
};
} }
if (this.node.continueOnFail) { if (this.node.continueOnFail) {
foundNodeSettings.push('continueOnFail'); foundNodeSettings.push('continueOnFail');
Vue.set(this.nodeValues, 'continueOnFail', this.node.continueOnFail); this.nodeValues = {
...this.nodeValues,
continueOnFail: this.node.continueOnFail,
};
} }
if (this.node.notesInFlow) { if (this.node.notesInFlow) {
foundNodeSettings.push('notesInFlow'); foundNodeSettings.push('notesInFlow');
Vue.set(this.nodeValues, 'notesInFlow', this.node.notesInFlow); this.nodeValues = {
...this.nodeValues,
notesInFlow: this.node.notesInFlow,
};
} }
if (this.node.retryOnFail) { if (this.node.retryOnFail) {
foundNodeSettings.push('retryOnFail'); foundNodeSettings.push('retryOnFail');
Vue.set(this.nodeValues, 'retryOnFail', this.node.retryOnFail); this.nodeValues = {
...this.nodeValues,
retryOnFail: this.node.retryOnFail,
};
} }
if (this.node.maxTries) { if (this.node.maxTries) {
foundNodeSettings.push('maxTries'); foundNodeSettings.push('maxTries');
Vue.set(this.nodeValues, 'maxTries', this.node.maxTries); this.nodeValues = {
...this.nodeValues,
maxTries: this.node.maxTries,
};
} }
if (this.node.waitBetweenTries) { if (this.node.waitBetweenTries) {
foundNodeSettings.push('waitBetweenTries'); foundNodeSettings.push('waitBetweenTries');
Vue.set(this.nodeValues, 'waitBetweenTries', this.node.waitBetweenTries); this.nodeValues = {
...this.nodeValues,
waitBetweenTries: this.node.waitBetweenTries,
};
} }
// Set default node settings // Set default node settings
for (const nodeSetting of this.nodeSettings) { for (const nodeSetting of this.nodeSettings) {
if (!foundNodeSettings.includes(nodeSetting.name)) { if (!foundNodeSettings.includes(nodeSetting.name)) {
// Set default value // Set default value
Vue.set(this.nodeValues, nodeSetting.name, nodeSetting.default); this.nodeValues = {
...this.nodeValues,
[nodeSetting.name]: nodeSetting.default,
};
} }
} }
Vue.set(this.nodeValues, 'parameters', deepCopy(this.node.parameters)); this.nodeValues = {
...this.nodeValues,
parameters: deepCopy(this.node.parameters),
};
} else { } else {
this.nodeValid = false; this.nodeValid = false;
} }

View File

@@ -194,7 +194,7 @@ import {
defaultMessageEventBusDestinationSentryOptions, defaultMessageEventBusDestinationSentryOptions,
} from 'n8n-workflow'; } from 'n8n-workflow';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import Vue, { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { LOG_STREAM_MODAL_KEY, MODAL_CONFIRM } from '@/constants'; import { LOG_STREAM_MODAL_KEY, MODAL_CONFIRM } from '@/constants';
import Modal from '@/components/Modal.vue'; import Modal from '@/components/Modal.vue';
import { useMessage } from '@/composables'; import { useMessage } from '@/composables';
@@ -249,7 +249,9 @@ export default defineComponent({
showRemoveConfirm: false, showRemoveConfirm: false,
typeSelectValue: '', typeSelectValue: '',
typeSelectPlaceholder: 'Destination Type', typeSelectPlaceholder: 'Destination Type',
nodeParameters: deepCopy(defaultMessageEventBusDestinationOptions), nodeParameters: deepCopy(
defaultMessageEventBusDestinationOptions,
) as MessageEventBusDestinationOptions,
webhookDescription: webhookModalDescription, webhookDescription: webhookModalDescription,
sentryDescription: sentryModalDescription, sentryDescription: sentryModalDescription,
syslogDescription: syslogModalDescription, syslogDescription: syslogModalDescription,
@@ -400,13 +402,13 @@ export default defineComponent({
// Apply the new value // Apply the new value
if (parameterData.value === undefined && parameterPathArray !== null) { if (parameterData.value === undefined && parameterPathArray !== null) {
// Delete array item // Delete array item
const path = parameterPathArray[1]; const path = parameterPathArray[1] as keyof MessageEventBusDestinationOptions;
const index = parameterPathArray[2]; const index = parameterPathArray[2];
const data = get(nodeParameters, path); const data = get(nodeParameters, path);
if (Array.isArray(data)) { if (Array.isArray(data)) {
data.splice(parseInt(index, 10), 1); data.splice(parseInt(index, 10), 1);
Vue.set(nodeParameters, path, data); nodeParameters[path] = data as never;
} }
} else { } else {
if (newValue === undefined) { if (newValue === undefined) {

View File

@@ -39,7 +39,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue, { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import type { ITag } from '@/Interface'; import type { ITag } from '@/Interface';
import IntersectionObserver from './IntersectionObserver.vue'; import IntersectionObserver from './IntersectionObserver.vue';
@@ -109,7 +109,7 @@ export default defineComponent({
methods: { methods: {
onObserved({ el, isIntersecting }: { el: HTMLElement; isIntersecting: boolean }) { onObserved({ el, isIntersecting }: { el: HTMLElement; isIntersecting: boolean }) {
if (el.dataset.id) { if (el.dataset.id) {
Vue.set(this.$data.visibility, el.dataset.id, isIntersecting); this.$data.visibility = { ...this.$data.visibility, [el.dataset.id]: isIntersecting };
} }
}, },
onClick(e: MouseEvent, tag: TagEl) { onClick(e: MouseEvent, tag: TagEl) {

View File

@@ -337,7 +337,6 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import Vue from 'vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { mapStores } from 'pinia'; import { mapStores } from 'pinia';
@@ -537,7 +536,7 @@ export default defineComponent({
workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout; workflowSettings.maxExecutionTimeout = this.rootStore.maxExecutionTimeout;
} }
Vue.set(this, 'workflowSettings', workflowSettings); this.workflowSettings = workflowSettings;
this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout); this.timeoutHMS = this.convertToHMS(workflowSettings.executionTimeout);
this.isLoading = false; this.isLoading = false;
@@ -752,7 +751,7 @@ export default defineComponent({
} }
}, },
async loadWorkflows() { async loadWorkflows() {
const workflows = await this.workflowsStore.fetchAllWorkflows(); const workflows = (await this.workflowsStore.fetchAllWorkflows()) as IWorkflowShortResponse[];
workflows.sort((a, b) => { workflows.sort((a, b) => {
if (a.name.toLowerCase() < b.name.toLowerCase()) { if (a.name.toLowerCase() < b.name.toLowerCase()) {
return -1; return -1;
@@ -763,13 +762,12 @@ export default defineComponent({
return 0; return 0;
}); });
// @ts-ignore
workflows.unshift({ workflows.unshift({
id: undefined as unknown as string, id: undefined as unknown as string,
name: this.$locale.baseText('workflowSettings.noWorkflow'), name: this.$locale.baseText('workflowSettings.noWorkflow'),
}); } as IWorkflowShortResponse);
Vue.set(this, 'workflows', workflows); this.workflows = workflows;
}, },
async saveSettings() { async saveSettings() {
// Set that the active state should be changed // Set that the active state should be changed

View File

@@ -220,10 +220,13 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
}, },
upsertCredential(credential: ICredentialsResponse): void { upsertCredential(credential: ICredentialsResponse): void {
if (credential.id) { if (credential.id) {
Vue.set(this.credentials, credential.id, { this.credentials = {
...this.credentials,
[credential.id]: {
...this.credentials[credential.id], ...this.credentials[credential.id],
...credential, ...credential,
}); },
};
} }
}, },
enableOAuthCredential(credential: ICredentialsResponse): void { enableOAuthCredential(credential: ICredentialsResponse): void {
@@ -351,31 +354,38 @@ export const useCredentialsStore = defineStore(STORES.CREDENTIALS, {
// Enterprise edition actions // Enterprise edition actions
setCredentialOwnedBy(payload: { credentialId: string; ownedBy: Partial<IUser> }) { setCredentialOwnedBy(payload: { credentialId: string; ownedBy: Partial<IUser> }) {
Vue.set(this.credentials[payload.credentialId], 'ownedBy', payload.ownedBy); this.credentials[payload.credentialId] = {
...this.credentials[payload.credentialId],
ownedBy: payload.ownedBy,
};
}, },
async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string }) { async setCredentialSharedWith(payload: { sharedWith: IUser[]; credentialId: string }) {
if (useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) { if (useSettingsStore().isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
await setCredentialSharedWith(useRootStore().getRestApiContext, payload.credentialId, { await setCredentialSharedWith(useRootStore().getRestApiContext, payload.credentialId, {
shareWithIds: payload.sharedWith.map((sharee) => sharee.id), shareWithIds: payload.sharedWith.map((sharee) => sharee.id),
}); });
Vue.set(this.credentials[payload.credentialId], 'sharedWith', payload.sharedWith);
this.credentials[payload.credentialId] = {
...this.credentials[payload.credentialId],
sharedWith: payload.sharedWith,
};
} }
}, },
addCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void { addCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
Vue.set( this.credentials[payload.credentialId] = {
this.credentials[payload.credentialId], ...this.credentials[payload.credentialId],
'sharedWith', sharedWith: (this.credentials[payload.credentialId].sharedWith || []).concat([
(this.credentials[payload.credentialId].sharedWith || []).concat([payload.sharee]), payload.sharee,
); ]),
};
}, },
removeCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void { removeCredentialSharee(payload: { credentialId: string; sharee: Partial<IUser> }): void {
Vue.set( this.credentials[payload.credentialId] = {
this.credentials[payload.credentialId], ...this.credentials[payload.credentialId],
'sharedWith', sharedWith: (this.credentials[payload.credentialId].sharedWith || []).filter(
(this.credentials[payload.credentialId].sharedWith || []).filter(
(sharee) => sharee.id !== payload.sharee.id, (sharee) => sharee.id !== payload.sharee.id,
), ),
); };
}, },
async getCredentialTranslation(credentialType: string): Promise<object> { async getCredentialTranslation(credentialType: string): Promise<object> {

View File

@@ -2,7 +2,6 @@ import { CLOUD_BASE_URL_PRODUCTION, CLOUD_BASE_URL_STAGING, STORES } from '@/con
import type { IRestApiContext, RootState } from '@/Interface'; import type { IRestApiContext, RootState } from '@/Interface';
import type { IDataObject } from 'n8n-workflow'; import type { IDataObject } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue';
import { useNodeTypesStore } from './nodeTypes.store'; import { useNodeTypesStore } from './nodeTypes.store';
const { VUE_APP_URL_BASE_API } = import.meta.env; const { VUE_APP_URL_BASE_API } = import.meta.env;
@@ -76,44 +75,44 @@ export const useRootStore = defineStore(STORES.ROOT, {
actions: { actions: {
setUrlBaseWebhook(urlBaseWebhook: string): void { setUrlBaseWebhook(urlBaseWebhook: string): void {
const url = urlBaseWebhook.endsWith('/') ? urlBaseWebhook : `${urlBaseWebhook}/`; const url = urlBaseWebhook.endsWith('/') ? urlBaseWebhook : `${urlBaseWebhook}/`;
Vue.set(this, 'urlBaseWebhook', url); this.urlBaseWebhook = url;
}, },
setUrlBaseEditor(urlBaseEditor: string): void { setUrlBaseEditor(urlBaseEditor: string): void {
const url = urlBaseEditor.endsWith('/') ? urlBaseEditor : `${urlBaseEditor}/`; const url = urlBaseEditor.endsWith('/') ? urlBaseEditor : `${urlBaseEditor}/`;
Vue.set(this, 'urlBaseEditor', url); this.urlBaseEditor = url;
}, },
setEndpointWebhook(endpointWebhook: string): void { setEndpointWebhook(endpointWebhook: string): void {
Vue.set(this, 'endpointWebhook', endpointWebhook); this.endpointWebhook = endpointWebhook;
}, },
setEndpointWebhookTest(endpointWebhookTest: string): void { setEndpointWebhookTest(endpointWebhookTest: string): void {
Vue.set(this, 'endpointWebhookTest', endpointWebhookTest); this.endpointWebhookTest = endpointWebhookTest;
}, },
setTimezone(timezone: string): void { setTimezone(timezone: string): void {
Vue.set(this, 'timezone', timezone); this.timezone = timezone;
}, },
setExecutionTimeout(executionTimeout: number): void { setExecutionTimeout(executionTimeout: number): void {
Vue.set(this, 'executionTimeout', executionTimeout); this.executionTimeout = executionTimeout;
}, },
setMaxExecutionTimeout(maxExecutionTimeout: number): void { setMaxExecutionTimeout(maxExecutionTimeout: number): void {
Vue.set(this, 'maxExecutionTimeout', maxExecutionTimeout); this.maxExecutionTimeout = maxExecutionTimeout;
}, },
setVersionCli(version: string): void { setVersionCli(version: string): void {
Vue.set(this, 'versionCli', version); this.versionCli = version;
}, },
setInstanceId(instanceId: string): void { setInstanceId(instanceId: string): void {
Vue.set(this, 'instanceId', instanceId); this.instanceId = instanceId;
}, },
setOauthCallbackUrls(urls: IDataObject): void { setOauthCallbackUrls(urls: IDataObject): void {
Vue.set(this, 'oauthCallbackUrls', urls); this.oauthCallbackUrls = urls;
}, },
setN8nMetadata(metadata: IDataObject): void { setN8nMetadata(metadata: IDataObject): void {
Vue.set(this, 'n8nMetadata', metadata); this.n8nMetadata = metadata as RootState['n8nMetadata'];
}, },
setDefaultLocale(locale: string): void { setDefaultLocale(locale: string): void {
Vue.set(this, 'defaultLocale', locale); this.defaultLocale = locale;
}, },
setIsNpmAvailable(isNpmAvailable: boolean): void { setIsNpmAvailable(isNpmAvailable: boolean): void {
Vue.set(this, 'isNpmAvailable', isNpmAvailable); this.isNpmAvailable = isNpmAvailable;
}, },
}, },
}); });

View File

@@ -8,7 +8,6 @@ import type {
} from '@/Interface'; } from '@/Interface';
import type { INodeIssues, IRunData } from 'n8n-workflow'; import type { INodeIssues, IRunData } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue';
import { useWorkflowsStore } from './workflows.store'; import { useWorkflowsStore } from './workflows.store';
export const useNDVStore = defineStore(STORES.NDV, { export const useNDVStore = defineStore(STORES.NDV, {
@@ -128,38 +127,47 @@ export const useNDVStore = defineStore(STORES.NDV, {
}, },
}, },
actions: { actions: {
setInputNodeName(name: string | undefined): void { setInputNodeName(nodeName: string | undefined): void {
Vue.set(this.input, 'nodeName', name); this.input = {
...this.input,
nodeName,
};
}, },
setInputRunIndex(run?: string): void { setInputRunIndex(run?: number): void {
Vue.set(this.input, 'run', run); this.input = {
...this.input,
run,
};
}, },
setMainPanelDimensions(params: { setMainPanelDimensions(params: {
panelType: string; panelType: string;
dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number }; dimensions: { relativeLeft?: number; relativeRight?: number; relativeWidth?: number };
}): void { }): void {
Vue.set(this.mainPanelDimensions, params.panelType, { this.mainPanelDimensions = {
...this.mainPanelDimensions,
[params.panelType]: {
...this.mainPanelDimensions[params.panelType], ...this.mainPanelDimensions[params.panelType],
...params.dimensions, ...params.dimensions,
}); },
};
}, },
setNDVSessionId(): void { setNDVSessionId(): void {
Vue.set(this, 'sessionId', `ndv-${Math.random().toString(36).slice(-8)}`); this.sessionId = `ndv-${Math.random().toString(36).slice(-8)}`;
}, },
resetNDVSessionId(): void { resetNDVSessionId(): void {
Vue.set(this, 'sessionId', ''); this.sessionId = '';
}, },
setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void { setPanelDisplayMode(params: { pane: NodePanelType; mode: IRunDataDisplayMode }): void {
Vue.set(this[params.pane], 'displayMode', params.mode); this[params.pane].displayMode = params.mode;
}, },
setOutputPanelEditModeEnabled(isEnabled: boolean): void { setOutputPanelEditModeEnabled(isEnabled: boolean): void {
Vue.set(this.output.editMode, 'enabled', isEnabled); this.output.editMode.enabled = isEnabled;
}, },
setOutputPanelEditModeValue(payload: string): void { setOutputPanelEditModeValue(payload: string): void {
Vue.set(this.output.editMode, 'value', payload); this.output.editMode.value = payload;
}, },
setMappableNDVInputFocus(paramName: string): void { setMappableNDVInputFocus(paramName: string): void {
Vue.set(this, 'focusedMappableInput', paramName); this.focusedMappableInput = paramName;
}, },
draggableStartDragging({ type, data }: { type: string; data: string }): void { draggableStartDragging({ type, data }: { type: string; data: string }): void {
this.draggable = { this.draggable = {
@@ -180,10 +188,10 @@ export const useNDVStore = defineStore(STORES.NDV, {
}; };
}, },
setDraggableStickyPos(position: XYPosition | null): void { setDraggableStickyPos(position: XYPosition | null): void {
Vue.set(this.draggable, 'stickyPosition', position); this.draggable.stickyPosition = position;
}, },
setDraggableCanDrop(canDrop: boolean): void { setDraggableCanDrop(canDrop: boolean): void {
Vue.set(this.draggable, 'canDrop', canDrop); this.draggable.canDrop = canDrop;
}, },
setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void { setMappingTelemetry(telemetry: { [key: string]: string | number | boolean }): void {
this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry }; this.mappingTelemetry = { ...this.mappingTelemetry, ...telemetry };
@@ -192,13 +200,13 @@ export const useNDVStore = defineStore(STORES.NDV, {
this.mappingTelemetry = {}; this.mappingTelemetry = {};
}, },
setHoveringItem(item: null | NDVState['hoveringItem']): void { setHoveringItem(item: null | NDVState['hoveringItem']): void {
Vue.set(this, 'hoveringItem', item); this.hoveringItem = item;
}, },
setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void { setNDVBranchIndex(e: { pane: 'input' | 'output'; branchIndex: number }): void {
Vue.set(this[e.pane], 'branch', e.branchIndex); this[e.pane].branch = e.branchIndex;
}, },
setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void { setNDVPanelDataIsEmpty(payload: { panel: 'input' | 'output'; isEmpty: boolean }): void {
Vue.set(this[payload.panel].data, 'isEmpty', payload.isEmpty); this[payload.panel].data.isEmpty = payload.isEmpty;
}, },
disableMappingHint(store = true) { disableMappingHint(store = true) {
this.isMappingOnboarded = true; this.isMappingOnboarded = true;
@@ -207,11 +215,19 @@ export const useNDVStore = defineStore(STORES.NDV, {
} }
}, },
updateNodeParameterIssues(issues: INodeIssues): void { updateNodeParameterIssues(issues: INodeIssues): void {
const activeNode = this.activeNode; const workflowsStore = useWorkflowsStore();
const activeNode = workflowsStore.getNodeByName(this.activeNodeName || '');
if (activeNode) { if (activeNode) {
Vue.set(activeNode, 'issues', { const nodeIndex = workflowsStore.workflow.nodes.findIndex((node) => {
return node.name === activeNode.name;
});
workflowsStore.updateNodeAtIndex(nodeIndex, {
issues: {
...activeNode.issues, ...activeNode.issues,
...issues, ...issues,
},
}); });
} }
}, },

View File

@@ -25,7 +25,6 @@ import type {
ResourceMapperFields, ResourceMapperFields,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue';
import { useCredentialsStore } from './credentials.store'; import { useCredentialsStore } from './credentials.store';
import { useRootStore } from './n8nRoot.store'; import { useRootStore } from './n8nRoot.store';
@@ -120,7 +119,7 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
}, },
{ ...this.nodeTypes }, { ...this.nodeTypes },
); );
Vue.set(this, 'nodeTypes', nodeTypes); this.nodeTypes = nodeTypes;
}, },
removeNodeTypes(nodeTypesToRemove: INodeTypeDescription[]): void { removeNodeTypes(nodeTypesToRemove: INodeTypeDescription[]): void {
this.nodeTypes = nodeTypesToRemove.reduce( this.nodeTypes = nodeTypesToRemove.reduce(

View File

@@ -26,7 +26,6 @@ import type {
WorkflowSettings, WorkflowSettings,
} from 'n8n-workflow'; } from 'n8n-workflow';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue';
import { useRootStore } from './n8nRoot.store'; import { useRootStore } from './n8nRoot.store';
import { useUIStore } from './ui.store'; import { useUIStore } from './ui.store';
import { useUsersStore } from './users.store'; import { useUsersStore } from './users.store';
@@ -220,13 +219,19 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
useVersionsStore().setVersionNotificationSettings(settings.versionNotifications); useVersionsStore().setVersionNotificationSettings(settings.versionNotifications);
}, },
stopShowingSetupPage(): void { stopShowingSetupPage(): void {
Vue.set(this.userManagement, 'showSetupOnFirstLoad', false); this.userManagement.showSetupOnFirstLoad = false;
}, },
disableTemplates(): void { disableTemplates(): void {
Vue.set(this.settings.templates, 'enabled', false); this.settings = {
...this.settings,
templates: {
...this.settings.templates,
enabled: false,
},
};
}, },
setPromptsData(promptsData: IN8nPrompts): void { setPromptsData(promptsData: IN8nPrompts): void {
Vue.set(this, 'promptsData', promptsData); this.promptsData = promptsData;
}, },
setAllowedModules(allowedModules: { builtIn?: string[]; external?: string[] }): void { setAllowedModules(allowedModules: { builtIn?: string[]; external?: string[] }): void {
this.settings.allowedModules = allowedModules; this.settings.allowedModules = allowedModules;
@@ -315,13 +320,13 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, {
return runLdapSync(rootStore.getRestApiContext, data); return runLdapSync(rootStore.getRestApiContext, data);
}, },
setSaveDataErrorExecution(newValue: string) { setSaveDataErrorExecution(newValue: string) {
Vue.set(this, 'saveDataErrorExecution', newValue); this.saveDataErrorExecution = newValue;
}, },
setSaveDataSuccessExecution(newValue: string) { setSaveDataSuccessExecution(newValue: string) {
Vue.set(this, 'saveDataSuccessExecution', newValue); this.saveDataSuccessExecution = newValue;
}, },
setSaveManualExecutions(saveManualExecutions: boolean) { setSaveManualExecutions(saveManualExecutions: boolean) {
Vue.set(this, 'saveManualExecutions', saveManualExecutions); this.saveManualExecutions = saveManualExecutions;
}, },
async getTimezones(): Promise<IDataObject> { async getTimezones(): Promise<IDataObject> {
const rootStore = useRootStore(); const rootStore = useRootStore();

View File

@@ -45,9 +45,15 @@ export const useTagsStore = defineStore(STORES.TAGS, {
...currentTag, ...currentTag,
...tag, ...tag,
}; };
Vue.set(this.tags, tagId, newTag); this.tags = {
...this.tags,
[tagId]: newTag,
};
} else { } else {
Vue.set(this.tags, tagId, tag); this.tags = {
...this.tags,
[tagId]: tag,
};
} }
}); });
}, },

View File

@@ -10,7 +10,6 @@ import type {
ITemplatesWorkflowFull, ITemplatesWorkflowFull,
IWorkflowTemplate, IWorkflowTemplate,
} from '@/Interface'; } from '@/Interface';
import Vue from 'vue';
import { useSettingsStore } from './settings.store'; import { useSettingsStore } from './settings.store';
import { import {
getCategories, getCategories,
@@ -104,27 +103,38 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
actions: { actions: {
addCategories(categories: ITemplatesCategory[]): void { addCategories(categories: ITemplatesCategory[]): void {
categories.forEach((category: ITemplatesCategory) => { categories.forEach((category: ITemplatesCategory) => {
Vue.set(this.categories, category.id, category); this.categories = {
...this.categories,
[category.id]: category,
};
}); });
}, },
addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void { addCollections(collections: Array<ITemplatesCollection | ITemplatesCollectionFull>): void {
collections.forEach((collection) => { collections.forEach((collection) => {
const workflows = (collection.workflows || []).map((workflow) => ({ id: workflow.id })); const workflows = (collection.workflows || []).map((workflow) => ({ id: workflow.id }));
const cachedCollection = this.collections[collection.id] || {}; const cachedCollection = this.collections[collection.id] || {};
Vue.set(this.collections, collection.id, {
this.collections = {
...this.collections,
[collection.id]: {
...cachedCollection, ...cachedCollection,
...collection, ...collection,
workflows, workflows,
}); },
};
}); });
}, },
addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void { addWorkflows(workflows: Array<ITemplatesWorkflow | ITemplatesWorkflowFull>): void {
workflows.forEach((workflow: ITemplatesWorkflow) => { workflows.forEach((workflow: ITemplatesWorkflow) => {
const cachedWorkflow = this.workflows[workflow.id] || {}; const cachedWorkflow = this.workflows[workflow.id] || {};
Vue.set(this.workflows, workflow.id, {
this.workflows = {
...this.workflows,
[workflow.id]: {
...cachedWorkflow, ...cachedWorkflow,
...workflow, ...workflow,
}); },
};
}); });
}, },
addCollectionSearch(data: { addCollectionSearch(data: {
@@ -133,9 +143,13 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
}): void { }): void {
const collectionIds = data.collections.map((collection) => collection.id); const collectionIds = data.collections.map((collection) => collection.id);
const searchKey = getSearchKey(data.query); const searchKey = getSearchKey(data.query);
Vue.set(this.collectionSearches, searchKey, {
this.collectionSearches = {
...this.collectionSearches,
[searchKey]: {
collectionIds, collectionIds,
}); },
};
}, },
addWorkflowsSearch(data: { addWorkflowsSearch(data: {
totalWorkflows: number; totalWorkflows: number;
@@ -146,18 +160,24 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
const searchKey = getSearchKey(data.query); const searchKey = getSearchKey(data.query);
const cachedResults = this.workflowSearches[searchKey]; const cachedResults = this.workflowSearches[searchKey];
if (!cachedResults) { if (!cachedResults) {
Vue.set(this.workflowSearches, searchKey, { this.workflowSearches = {
workflowIds, ...this.workflowSearches,
[searchKey]: {
workflowIds: workflowIds as unknown as string[],
totalWorkflows: data.totalWorkflows, totalWorkflows: data.totalWorkflows,
}); },
};
return; return;
} }
Vue.set(this.workflowSearches, searchKey, { this.workflowSearches = {
workflowIds: [...cachedResults.workflowIds, ...workflowIds], ...this.workflowSearches,
[searchKey]: {
workflowIds: [...cachedResults.workflowIds, ...workflowIds] as string[],
totalWorkflows: data.totalWorkflows, totalWorkflows: data.totalWorkflows,
}); },
};
}, },
setWorkflowSearchLoading(query: ITemplatesQuery): void { setWorkflowSearchLoading(query: ITemplatesQuery): void {
const searchKey = getSearchKey(query); const searchKey = getSearchKey(query);
@@ -166,7 +186,10 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
return; return;
} }
Vue.set(this.workflowSearches[searchKey], 'loadingMore', true); this.workflowSearches[searchKey] = {
...this.workflowSearches[searchKey],
loadingMore: true,
};
}, },
setWorkflowSearchLoaded(query: ITemplatesQuery): void { setWorkflowSearchLoaded(query: ITemplatesQuery): void {
const searchKey = getSearchKey(query); const searchKey = getSearchKey(query);
@@ -175,7 +198,10 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
return; return;
} }
Vue.set(this.workflowSearches[searchKey], 'loadingMore', false); this.workflowSearches[searchKey] = {
...this.workflowSearches[searchKey],
loadingMore: false,
};
}, },
resetSessionId(): void { resetSessionId(): void {
this.previousSessionId = this.currentSessionId; this.previousSessionId = this.currentSessionId;

View File

@@ -42,7 +42,6 @@ import type {
UIState, UIState,
XYPosition, XYPosition,
} from '@/Interface'; } from '@/Interface';
import Vue from 'vue';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { useRootStore } from './n8nRoot.store'; import { useRootStore } from './n8nRoot.store';
import { getCurlToJson } from '@/api/curlHelper'; import { getCurlToJson } from '@/api/curlHelper';
@@ -51,6 +50,7 @@ import { useSettingsStore } from './settings.store';
import { useCloudPlanStore } from './cloudPlan.store'; import { useCloudPlanStore } from './cloudPlan.store';
import type { BaseTextKey } from '@/plugins/i18n'; import type { BaseTextKey } from '@/plugins/i18n';
import { i18n as locale } from '@/plugins/i18n'; import { i18n as locale } from '@/plugins/i18n';
import type { Modals, NewCredentialsModal } from '@/Interface';
import { useTelemetryStore } from '@/stores/telemetry.store'; import { useTelemetryStore } from '@/stores/telemetry.store';
export const useUIStore = defineStore(STORES.UI, { export const useUIStore = defineStore(STORES.UI, {
@@ -332,36 +332,57 @@ export const useUIStore = defineStore(STORES.UI, {
}, },
}, },
actions: { actions: {
setMode(name: string, mode: string): void { setMode(name: keyof Modals, mode: string): void {
Vue.set(this.modals[name], 'mode', mode); this.modals[name] = {
...this.modals[name],
mode,
};
}, },
setActiveId(name: string, id: string): void { setActiveId(name: keyof Modals, activeId: string): void {
Vue.set(this.modals[name], 'activeId', id); this.modals[name] = {
...this.modals[name],
activeId,
};
}, },
setShowAuthSelector(name: string, show: boolean) { setShowAuthSelector(name: keyof Modals, showAuthSelector: boolean) {
Vue.set(this.modals[name], 'showAuthSelector', show); this.modals[name] = {
...this.modals[name],
showAuthSelector,
} as NewCredentialsModal;
}, },
setModalData(payload: { name: string; data: Record<string, unknown> }) { setModalData(payload: { name: keyof Modals; data: Record<string, unknown> }) {
Vue.set(this.modals[payload.name], 'data', payload.data); this.modals[payload.name] = {
...this.modals[payload.name],
data: payload.data,
};
}, },
openModal(name: string): void { openModal(name: keyof Modals): void {
Vue.set(this.modals[name], 'open', true); this.modals[name] = {
this.modalStack = [name].concat(this.modalStack); ...this.modals[name],
open: true,
};
this.modalStack = [name].concat(this.modalStack) as string[];
}, },
openModalWithData(payload: { name: string; data: Record<string, unknown> }): void { openModalWithData(payload: { name: keyof Modals; data: Record<string, unknown> }): void {
this.setModalData(payload); this.setModalData(payload);
this.openModal(payload.name); this.openModal(payload.name);
}, },
closeModal(name: string): void { closeModal(name: keyof Modals): void {
Vue.set(this.modals[name], 'open', false); this.modals[name] = {
...this.modals[name],
open: false,
};
this.modalStack = this.modalStack.filter((openModalName: string) => { this.modalStack = this.modalStack.filter((openModalName: string) => {
return name !== openModalName; return name !== openModalName;
}); });
}, },
closeAllModals(): void { closeAllModals(): void {
Object.keys(this.modals).forEach((name: string) => { Object.keys(this.modals).forEach((name) => {
if (this.modals[name].open) { if (this.modals[name].open) {
Vue.set(this.modals[name], 'open', false); this.modals[name] = {
...this.modals[name],
open: false,
};
} }
}); });
this.modalStack = []; this.modalStack = [];
@@ -385,10 +406,16 @@ export const useUIStore = defineStore(STORES.UI, {
}; };
}, },
setDraggableStickyPos(position: XYPosition): void { setDraggableStickyPos(position: XYPosition): void {
Vue.set(this.draggable, 'stickyPosition', position); this.draggable = {
...this.draggable,
stickyPosition: position,
};
}, },
setDraggableCanDrop(canDrop: boolean): void { setDraggableCanDrop(canDrop: boolean): void {
Vue.set(this.draggable, 'canDrop', canDrop); this.draggable = {
...this.draggable,
canDrop,
};
}, },
openDeleteUserModal(id: string): void { openDeleteUserModal(id: string): void {
this.setActiveId(DELETE_USER_MODAL_KEY, id); this.setActiveId(DELETE_USER_MODAL_KEY, id);
@@ -460,17 +487,23 @@ export const useUIStore = defineStore(STORES.UI, {
} }
}, },
resetSelectedNodes(): void { resetSelectedNodes(): void {
Vue.set(this, 'selectedNodes', []); this.selectedNodes = [];
}, },
addSidebarMenuItems(menuItems: IMenuItem[]) { addSidebarMenuItems(menuItems: IMenuItem[]) {
const updated = this.sidebarMenuItems.concat(menuItems); const updated = this.sidebarMenuItems.concat(menuItems);
Vue.set(this, 'sidebarMenuItems', updated); this.sidebarMenuItems = updated;
}, },
setCurlCommand(payload: { name: string; command: string }): void { setCurlCommand(payload: { name: string; command: string }): void {
Vue.set(this.modals[payload.name], 'curlCommand', payload.command); this.modals[payload.name] = {
...this.modals[payload.name],
curlCommand: payload.command,
};
}, },
setHttpNodeParameters(payload: { name: string; parameters: string }): void { setHttpNodeParameters(payload: { name: string; parameters: string }): void {
Vue.set(this.modals[payload.name], 'httpNodeParameters', payload.parameters); this.modals[payload.name] = {
...this.modals[payload.name],
httpNodeParameters: payload.parameters,
};
}, },
toggleSidebarMenuCollapse(): void { toggleSidebarMenuCollapse(): void {
this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed; this.sidebarMenuCollapsed = !this.sidebarMenuCollapsed;

View File

@@ -35,7 +35,6 @@ import type {
import { getCredentialPermissions } from '@/permissions'; import { getCredentialPermissions } from '@/permissions';
import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils'; import { getPersonalizedNodeTypes, isAuthorized, PERMISSIONS, ROLE } from '@/utils';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import Vue from 'vue';
import { useRootStore } from './n8nRoot.store'; import { useRootStore } from './n8nRoot.store';
import { usePostHog } from './posthog.store'; import { usePostHog } from './posthog.store';
import { useSettingsStore } from './settings.store'; import { useSettingsStore } from './settings.store';
@@ -133,17 +132,29 @@ export const useUsersStore = defineStore(STORES.USERS, {
isPendingUser: isPendingUser(updatedUser), isPendingUser: isPendingUser(updatedUser),
isOwner: updatedUser.globalRole?.name === ROLE.Owner, isOwner: updatedUser.globalRole?.name === ROLE.Owner,
}; };
Vue.set(this.users, user.id, user);
this.users = {
...this.users,
[user.id]: user,
};
}); });
}, },
deleteUserById(userId: string): void { deleteUserById(userId: string): void {
Vue.delete(this.users, userId); const { [userId]: _, ...users } = this.users;
this.users = users;
}, },
setPersonalizationAnswers(answers: IPersonalizationLatestVersion): void { setPersonalizationAnswers(answers: IPersonalizationLatestVersion): void {
if (!this.currentUser) { if (!this.currentUser) {
return; return;
} }
Vue.set(this.currentUser, 'personalizationAnswers', answers);
this.users = {
...this.users,
[this.currentUser.id]: {
...this.currentUser,
personalizationAnswers: answers,
},
};
}, },
async loginWithCookie(): Promise<void> { async loginWithCookie(): Promise<void> {
const rootStore = useRootStore(); const rootStore = useRootStore();

View File

@@ -1,4 +1,3 @@
import Vue from 'vue';
import type { IUser } from '../Interface'; import type { IUser } from '../Interface';
import { setWorkflowSharedWith } from '@/api/workflows.ee'; import { setWorkflowSharedWith } from '@/api/workflows.ee';
import { EnterpriseEditionFeature, STORES } from '@/constants'; import { EnterpriseEditionFeature, STORES } from '@/constants';
@@ -29,8 +28,14 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
setWorkflowOwnedBy(payload: { workflowId: string; ownedBy: Partial<IUser> }): void { setWorkflowOwnedBy(payload: { workflowId: string; ownedBy: Partial<IUser> }): void {
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'ownedBy', payload.ownedBy); workflowsStore.workflowsById[payload.workflowId] = {
Vue.set(workflowsStore.workflow, 'ownedBy', payload.ownedBy); ...workflowsStore.workflowsById[payload.workflowId],
ownedBy: payload.ownedBy,
};
workflowsStore.workflow = {
...workflowsStore.workflow,
ownedBy: payload.ownedBy,
};
}, },
setWorkflowSharedWith(payload: { setWorkflowSharedWith(payload: {
workflowId: string; workflowId: string;
@@ -38,30 +43,34 @@ export const useWorkflowsEEStore = defineStore(STORES.WORKFLOWS_EE, {
}): void { }): void {
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
Vue.set(workflowsStore.workflowsById[payload.workflowId], 'sharedWith', payload.sharedWith); workflowsStore.workflowsById[payload.workflowId] = {
Vue.set(workflowsStore.workflow, 'sharedWith', payload.sharedWith); ...workflowsStore.workflowsById[payload.workflowId],
sharedWith: payload.sharedWith,
};
workflowsStore.workflow = {
...workflowsStore.workflow,
sharedWith: payload.sharedWith,
};
}, },
addWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void { addWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
Vue.set( workflowsStore.workflowsById[payload.workflowId] = {
workflowsStore.workflowsById[payload.workflowId], ...workflowsStore.workflowsById[payload.workflowId],
'sharedWith', sharedWith: (workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).concat([
payload.sharee, payload.sharee,
]), ]),
); };
}, },
removeWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void { removeWorkflowSharee(payload: { workflowId: string; sharee: Partial<IUser> }): void {
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
Vue.set( workflowsStore.workflowsById[payload.workflowId] = {
workflowsStore.workflowsById[payload.workflowId], ...workflowsStore.workflowsById[payload.workflowId],
'sharedWith', sharedWith: (workflowsStore.workflowsById[payload.workflowId].sharedWith || []).filter(
(workflowsStore.workflowsById[payload.workflowId].sharedWith || []).filter(
(sharee) => sharee.id !== payload.sharee.id, (sharee) => sharee.id !== payload.sharee.id,
), ),
); };
}, },
async saveWorkflowSharedWith(payload: { async saveWorkflowSharedWith(payload: {
sharedWith: Array<Partial<IUser>>; sharedWith: Array<Partial<IUser>>;

View File

@@ -18,6 +18,7 @@ import type {
IExecutionsListResponse, IExecutionsListResponse,
IExecutionsStopData, IExecutionsStopData,
INewWorkflowData, INewWorkflowData,
INodeMetadata,
INodeUi, INodeUi,
INodeUpdatePropertiesInformation, INodeUpdatePropertiesInformation,
IPushDataExecutionFinished, IPushDataExecutionFinished,
@@ -26,6 +27,7 @@ import type {
IStartRunData, IStartRunData,
IUpdateInformation, IUpdateInformation,
IUsedCredential, IUsedCredential,
IUser,
IWorkflowDataUpdate, IWorkflowDataUpdate,
IWorkflowDb, IWorkflowDb,
IWorkflowsMap, IWorkflowsMap,
@@ -44,6 +46,7 @@ import type {
INodeCredentialsDetails, INodeCredentialsDetails,
INodeExecutionData, INodeExecutionData,
INodeIssueData, INodeIssueData,
INodeIssueObjectProperty,
INodeParameters, INodeParameters,
INodeTypeData, INodeTypeData,
INodeTypes, INodeTypes,
@@ -83,6 +86,7 @@ import { useNodeTypesStore } from './nodeTypes.store';
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store'; import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
import { useUsersStore } from '@/stores/users.store'; import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store'; import { useSettingsStore } from '@/stores/settings.store';
import type { NodeMetadataMap } from '@/Interface';
const createEmptyWorkflow = (): IWorkflowDb => ({ const createEmptyWorkflow = (): IWorkflowDb => ({
id: PLACEHOLDER_EMPTY_WORKFLOW_ID, id: PLACEHOLDER_EMPTY_WORKFLOW_ID,
@@ -416,7 +420,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
this.workflow = createEmptyWorkflow(); this.workflow = createEmptyWorkflow();
if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) { if (settingsStore.isEnterpriseFeatureEnabled(EnterpriseEditionFeature.Sharing)) {
Vue.set(this.workflow, 'ownedBy', usersStore.currentUser); this.workflow = {
...this.workflow,
ownedBy: usersStore.currentUser as IUser,
};
} }
}, },
@@ -509,10 +516,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
}, },
addWorkflow(workflow: IWorkflowDb): void { addWorkflow(workflow: IWorkflowDb): void {
Vue.set(this.workflowsById, workflow.id, { this.workflowsById = {
...this.workflowsById,
[workflow.id]: {
...this.workflowsById[workflow.id], ...this.workflowsById[workflow.id],
...deepCopy(workflow), ...deepCopy(workflow),
}); },
};
}, },
setWorkflowActive(workflowId: string): void { setWorkflowActive(workflowId: string): void {
@@ -576,57 +586,60 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
}, },
setWorkflowSettings(workflowSettings: IWorkflowSettings): void { setWorkflowSettings(workflowSettings: IWorkflowSettings): void {
Vue.set(this.workflow, 'settings', workflowSettings); this.workflow = {
...this.workflow,
settings: workflowSettings as IWorkflowDb['settings'],
};
}, },
setWorkflowPinData(pinData: IPinData): void { setWorkflowPinData(pinData: IPinData): void {
Vue.set(this.workflow, 'pinData', pinData || {}); this.workflow = {
...this.workflow,
pinData: pinData || {},
};
dataPinningEventBus.emit('pin-data', pinData || {}); dataPinningEventBus.emit('pin-data', pinData || {});
}, },
setWorkflowTagIds(tags: string[]): void { setWorkflowTagIds(tags: string[]): void {
Vue.set(this.workflow, 'tags', tags); this.workflow = {
...this.workflow,
tags,
};
}, },
addWorkflowTagIds(tags: string[]): void { addWorkflowTagIds(tags: string[]): void {
Vue.set(this.workflow, 'tags', [...new Set([...(this.workflow.tags || []), ...tags])]); this.workflow = {
...this.workflow,
tags: [...new Set([...(this.workflow.tags || []), ...tags])] as IWorkflowDb['tags'],
};
}, },
removeWorkflowTagId(tagId: string): void { removeWorkflowTagId(tagId: string): void {
const tags = this.workflow.tags as string[]; const tags = this.workflow.tags as string[];
const updated = tags.filter((id: string) => id !== tagId); const updated = tags.filter((id: string) => id !== tagId);
Vue.set(this.workflow, 'tags', updated); this.workflow = {
...this.workflow,
tags: updated as IWorkflowDb['tags'],
};
}, },
setWorkflow(workflow: IWorkflowDb): void { setWorkflow(workflow: IWorkflowDb): void {
Vue.set(this, 'workflow', workflow); this.workflow = workflow;
this.workflow = {
if (!this.workflow.hasOwnProperty('active')) { ...this.workflow,
Vue.set(this.workflow, 'active', false); ...(!this.workflow.hasOwnProperty('active') ? { active: false } : {}),
} ...(!this.workflow.hasOwnProperty('connections') ? { connections: {} } : {}),
if (!this.workflow.hasOwnProperty('connections')) { ...(!this.workflow.hasOwnProperty('createdAt') ? { createdAt: -1 } : {}),
Vue.set(this.workflow, 'connections', {}); ...(!this.workflow.hasOwnProperty('updatedAt') ? { updatedAt: -1 } : {}),
} ...(!this.workflow.hasOwnProperty('id') ? { id: PLACEHOLDER_EMPTY_WORKFLOW_ID } : {}),
if (!this.workflow.hasOwnProperty('createdAt')) { ...(!this.workflow.hasOwnProperty('nodes') ? { nodes: [] } : {}),
Vue.set(this.workflow, 'createdAt', -1); ...(!this.workflow.hasOwnProperty('settings') ? { settings: {} } : {}),
} };
if (!this.workflow.hasOwnProperty('updatedAt')) {
Vue.set(this.workflow, 'updatedAt', -1);
}
if (!this.workflow.hasOwnProperty('id')) {
Vue.set(this.workflow, 'id', PLACEHOLDER_EMPTY_WORKFLOW_ID);
}
if (!this.workflow.hasOwnProperty('nodes')) {
Vue.set(this.workflow, 'nodes', []);
}
if (!this.workflow.hasOwnProperty('settings')) {
Vue.set(this.workflow, 'settings', {});
}
}, },
pinData(payload: { node: INodeUi; data: INodeExecutionData[] }): void { pinData(payload: { node: INodeUi; data: INodeExecutionData[] }): void {
if (!this.workflow.pinData) { if (!this.workflow.pinData) {
Vue.set(this.workflow, 'pinData', {}); this.workflow = { ...this.workflow, pinData: {} };
} }
if (!Array.isArray(payload.data)) { if (!Array.isArray(payload.data)) {
@@ -637,7 +650,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
isJsonKeyObject(item) ? item : { json: item }, isJsonKeyObject(item) ? item : { json: item },
); );
Vue.set(this.workflow.pinData!, payload.node.name, storedPinData); this.workflow = {
...this.workflow,
pinData: {
...this.workflow.pinData,
[payload.node.name]: storedPinData,
},
};
const uiStore = useUIStore(); const uiStore = useUIStore();
uiStore.stateIsDirty = true; uiStore.stateIsDirty = true;
@@ -647,11 +666,14 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
unpinData(payload: { node: INodeUi }): void { unpinData(payload: { node: INodeUi }): void {
if (!this.workflow.pinData) { if (!this.workflow.pinData) {
Vue.set(this.workflow, 'pinData', {}); this.workflow = { ...this.workflow, pinData: {} };
} }
Vue.set(this.workflow.pinData!, payload.node.name, undefined); const { [payload.node.name]: _, ...pinData } = this.workflow.pinData!;
delete this.workflow.pinData![payload.node.name]; this.workflow = {
...this.workflow,
pinData,
};
const uiStore = useUIStore(); const uiStore = useUIStore();
uiStore.stateIsDirty = true; uiStore.stateIsDirty = true;
@@ -670,10 +692,25 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
// Check if source node and type exist already and if not add them // Check if source node and type exist already and if not add them
if (!this.workflow.connections.hasOwnProperty(sourceData.node)) { if (!this.workflow.connections.hasOwnProperty(sourceData.node)) {
Vue.set(this.workflow.connections, sourceData.node, {}); this.workflow = {
...this.workflow,
connections: {
...this.workflow.connections,
[sourceData.node]: {},
},
};
} }
if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) { if (!this.workflow.connections[sourceData.node].hasOwnProperty(sourceData.type)) {
Vue.set(this.workflow.connections[sourceData.node], sourceData.type, []); this.workflow = {
...this.workflow,
connections: {
...this.workflow.connections,
[sourceData.node]: {
...this.workflow.connections[sourceData.node],
[sourceData.type]: [],
},
},
};
} }
if ( if (
this.workflow.connections[sourceData.node][sourceData.type].length < this.workflow.connections[sourceData.node][sourceData.type].length <
@@ -817,12 +854,19 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
uiStore.lastSelectedNode = nameData.new; uiStore.lastSelectedNode = nameData.new;
} }
Vue.set(this.nodeMetadata, nameData.new, this.nodeMetadata[nameData.old]); this.nodeMetadata = { ...this.nodeMetadata, [nameData.new]: this.nodeMetadata[nameData.old] };
Vue.delete(this.nodeMetadata, nameData.old); Vue.delete(this.nodeMetadata, nameData.old);
if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(nameData.old)) { if (this.workflow.pinData && this.workflow.pinData.hasOwnProperty(nameData.old)) {
Vue.set(this.workflow.pinData, nameData.new, this.workflow.pinData[nameData.old]); this.workflow = {
Vue.delete(this.workflow.pinData, nameData.old); ...this.workflow,
pinData: {
...this.workflow.pinData,
[nameData.new]: this.workflow.pinData[nameData.old],
},
};
Vue.delete(this.workflow.pinData!, nameData.old);
} }
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData); this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
@@ -835,13 +879,31 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
return true; return true;
}, },
updateNodeAtIndex(nodeIndex: number, nodeData: Partial<INodeUi>): void {
if (nodeIndex !== -1) {
const node = this.workflow.nodes[nodeIndex];
this.workflow = {
...this.workflow,
nodes: [
...this.workflow.nodes.slice(0, nodeIndex),
{ ...node, ...nodeData },
...this.workflow.nodes.slice(nodeIndex + 1),
],
};
}
},
setNodeIssue(nodeIssueData: INodeIssueData): boolean { setNodeIssue(nodeIssueData: INodeIssueData): boolean {
const node = this.workflow.nodes.find((node) => { const nodeIndex = this.workflow.nodes.findIndex((node) => {
return node.name === nodeIssueData.node; return node.name === nodeIssueData.node;
}); });
if (!node) {
if (nodeIndex === -1) {
return false; return false;
} }
const node = this.workflow.nodes[nodeIndex!];
if (nodeIssueData.value === null) { if (nodeIssueData.value === null) {
// Remove the value if one exists // Remove the value if one exists
if (node.issues === undefined || node.issues[nodeIssueData.type] === undefined) { if (node.issues === undefined || node.issues[nodeIssueData.type] === undefined) {
@@ -853,10 +915,17 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
Vue.delete(node.issues, nodeIssueData.type); Vue.delete(node.issues, nodeIssueData.type);
} else { } else {
if (node.issues === undefined) { if (node.issues === undefined) {
Vue.set(node, 'issues', {}); this.updateNodeAtIndex(nodeIndex, {
issues: {},
});
} }
// Set/Overwrite the value
Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value); this.updateNodeAtIndex(nodeIndex, {
issues: {
...node.issues,
[nodeIssueData.type]: nodeIssueData.value as INodeIssueObjectProperty,
},
});
} }
return true; return true;
}, },
@@ -871,7 +940,7 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
this.workflow.nodes.push(nodeData); this.workflow.nodes.push(nodeData);
// Init node metadata // Init node metadata
if (!this.nodeMetadata[nodeData.name]) { if (!this.nodeMetadata[nodeData.name]) {
Vue.set(this.nodeMetadata, nodeData.name, {}); this.nodeMetadata = { ...this.nodeMetadata, [nodeData.name]: {} as INodeMetadata };
} }
}, },
@@ -899,7 +968,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
} }
if (data.removePinData) { if (data.removePinData) {
Vue.set(this.workflow, 'pinData', {}); this.workflow = {
...this.workflow,
pinData: {},
};
} }
this.workflow.nodes.splice(0, this.workflow.nodes.length); this.workflow.nodes.splice(0, this.workflow.nodes.length);
@@ -908,26 +980,29 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void { updateNodeProperties(updateInformation: INodeUpdatePropertiesInformation): void {
// Find the node that should be updated // Find the node that should be updated
const node = this.workflow.nodes.find((node) => { const nodeIndex = this.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name; return node.name === updateInformation.name;
}); });
if (node) { if (nodeIndex !== -1) {
for (const key of Object.keys(updateInformation.properties)) { for (const key of Object.keys(updateInformation.properties)) {
const uiStore = useUIStore(); const uiStore = useUIStore();
uiStore.stateIsDirty = true; uiStore.stateIsDirty = true;
Vue.set(node, key, updateInformation.properties[key]);
this.updateNodeAtIndex(nodeIndex, {
[key]: updateInformation.properties[key],
});
} }
} }
}, },
setNodeValue(updateInformation: IUpdateInformation): void { setNodeValue(updateInformation: IUpdateInformation): void {
// Find the node that should be updated // Find the node that should be updated
const node = this.workflow.nodes.find((node) => { const nodeIndex = this.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name; return node.name === updateInformation.name;
}); });
if (node === undefined || node === null || !updateInformation.key) { if (nodeIndex === -1 || !updateInformation.key) {
throw new Error( throw new Error(
`Node with the name "${updateInformation.name}" could not be found to set parameter.`, `Node with the name "${updateInformation.name}" could not be found to set parameter.`,
); );
@@ -935,21 +1010,26 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
const uiStore = useUIStore(); const uiStore = useUIStore();
uiStore.stateIsDirty = true; uiStore.stateIsDirty = true;
Vue.set(node, updateInformation.key, updateInformation.value);
this.updateNodeAtIndex(nodeIndex, {
[updateInformation.key]: updateInformation.value,
});
}, },
setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void { setNodeParameters(updateInformation: IUpdateInformation, append?: boolean): void {
// Find the node that should be updated // Find the node that should be updated
const node = this.workflow.nodes.find((node) => { const nodeIndex = this.workflow.nodes.findIndex((node) => {
return node.name === updateInformation.name; return node.name === updateInformation.name;
}); });
if (node === undefined || node === null) { if (nodeIndex === -1) {
throw new Error( throw new Error(
`Node with the name "${updateInformation.name}" could not be found to set parameter.`, `Node with the name "${updateInformation.name}" could not be found to set parameter.`,
); );
} }
const node = this.workflow.nodes[nodeIndex];
const uiStore = useUIStore(); const uiStore = useUIStore();
uiStore.stateIsDirty = true; uiStore.stateIsDirty = true;
const newParameters = const newParameters =
@@ -957,13 +1037,17 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
? { ...node.parameters, ...updateInformation.value } ? { ...node.parameters, ...updateInformation.value }
: updateInformation.value; : updateInformation.value;
Vue.set(node, 'parameters', newParameters); this.updateNodeAtIndex(nodeIndex, {
parameters: newParameters as INodeParameters,
});
if (!this.nodeMetadata[node.name]) { this.nodeMetadata = {
Vue.set(this.nodeMetadata, node.name, {}); ...this.nodeMetadata,
} [node.name]: {
...this.nodeMetadata[node.name],
Vue.set(this.nodeMetadata[node.name], 'parametersLastUpdatedAt', Date.now()); parametersLastUpdatedAt: Date.now(),
},
} as NodeMetadataMap;
}, },
setLastNodeParameters(updateInformation: IUpdateInformation) { setLastNodeParameters(updateInformation: IUpdateInformation) {
@@ -989,9 +1073,21 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
throw new Error('The "workflowExecutionData" is not initialized!'); throw new Error('The "workflowExecutionData" is not initialized!');
} }
if (this.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) { if (this.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
Vue.set(this.workflowExecutionData.data.resultData.runData, pushData.nodeName, []); this.workflowExecutionData = {
...this.workflowExecutionData,
data: {
...this.workflowExecutionData.data,
resultData: {
...this.workflowExecutionData.data.resultData,
runData: {
...this.workflowExecutionData.data.resultData.runData,
[pushData.nodeName]: [],
},
},
},
};
} }
this.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data); this.workflowExecutionData.data!.resultData.runData[pushData.nodeName].push(pushData.data);
this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData); this.workflowExecutionPairedItemMappings = getPairedItemsMapping(this.workflowExecutionData);
}, },
clearNodeExecutionData(nodeName: string): void { clearNodeExecutionData(nodeName: string): void {
@@ -1033,28 +1129,37 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
finishedActiveExecution: IPushDataExecutionFinished | IPushDataUnsavedExecutionFinished, finishedActiveExecution: IPushDataExecutionFinished | IPushDataUnsavedExecutionFinished,
): void { ): void {
// Find the execution to set to finished // Find the execution to set to finished
const activeExecution = this.activeExecutions.find((execution) => { const activeExecutionIndex = this.activeExecutions.findIndex((execution) => {
return execution.id === finishedActiveExecution.executionId; return execution.id === finishedActiveExecution.executionId;
}); });
if (activeExecution === undefined) { if (activeExecutionIndex === -1) {
// The execution could not be found // The execution could not be found
return; return;
} }
if (finishedActiveExecution.executionId !== undefined) { const activeExecution = this.activeExecutions[activeExecutionIndex];
Vue.set(activeExecution, 'id', finishedActiveExecution.executionId);
} this.activeExecutions = [
...this.activeExecutions.slice(0, activeExecutionIndex),
{
...activeExecution,
...(finishedActiveExecution.executionId !== undefined
? { id: finishedActiveExecution.executionId }
: {}),
finished: finishedActiveExecution.data.finished,
stoppedAt: finishedActiveExecution.data.stoppedAt,
},
...this.activeExecutions.slice(activeExecutionIndex + 1),
];
Vue.set(activeExecution, 'finished', finishedActiveExecution.data.finished);
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
if (finishedActiveExecution.data && (finishedActiveExecution.data as IRun).data) { if (finishedActiveExecution.data && (finishedActiveExecution.data as IRun).data) {
this.setWorkflowExecutionRunData((finishedActiveExecution.data as IRun).data); this.setWorkflowExecutionRunData((finishedActiveExecution.data as IRun).data);
} }
}, },
setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]): void { setActiveExecutions(newActiveExecutions: IExecutionsCurrentSummaryExtended[]): void {
Vue.set(this, 'activeExecutions', newActiveExecutions); this.activeExecutions = newActiveExecutions;
}, },
async retryExecution(id: string, loadWorkflow?: boolean): Promise<boolean> { async retryExecution(id: string, loadWorkflow?: boolean): Promise<boolean> {
@@ -1247,7 +1352,13 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
}, },
setNodePristine(nodeName: string, isPristine: boolean): void { setNodePristine(nodeName: string, isPristine: boolean): void {
Vue.set(this.nodeMetadata[nodeName], 'pristine', isPristine); this.nodeMetadata = {
...this.nodeMetadata,
[nodeName]: {
...this.nodeMetadata[nodeName],
pristine: isPristine,
},
};
}, },
}, },
}); });