mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-12 21:37:32 -08:00
fix(editor): Fix RLC not loading when an expression can't resolve (#7295)
Also fixes label (list -> From list) Github issue / Community forum post (link here to close automatically):
This commit is contained in:
parent
1b4848afcb
commit
ddc26c21bd
|
@ -7,6 +7,7 @@ const credentialsModal = new CredentialsModal();
|
||||||
|
|
||||||
const NO_CREDENTIALS_MESSAGE = 'Please add your credential';
|
const NO_CREDENTIALS_MESSAGE = 'Please add your credential';
|
||||||
const INVALID_CREDENTIALS_MESSAGE = 'Please check your credential';
|
const INVALID_CREDENTIALS_MESSAGE = 'Please check your credential';
|
||||||
|
const MODE_SELECTOR_LIST = 'From list';
|
||||||
|
|
||||||
describe('Resource Locator', () => {
|
describe('Resource Locator', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -18,6 +19,14 @@ describe('Resource Locator', () => {
|
||||||
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
|
||||||
ndv.getters.resourceLocator('documentId').should('be.visible');
|
ndv.getters.resourceLocator('documentId').should('be.visible');
|
||||||
ndv.getters.resourceLocator('sheetName').should('be.visible');
|
ndv.getters.resourceLocator('sheetName').should('be.visible');
|
||||||
|
ndv.getters
|
||||||
|
.resourceLocatorModeSelector('documentId')
|
||||||
|
.find('input')
|
||||||
|
.should('have.value', MODE_SELECTOR_LIST);
|
||||||
|
ndv.getters
|
||||||
|
.resourceLocatorModeSelector('sheetName')
|
||||||
|
.find('input')
|
||||||
|
.should('have.value', MODE_SELECTOR_LIST);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show appropriate error when credentials are not set', () => {
|
it('should show appropriate error when credentials are not set', () => {
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
v-for="mode in parameter.modes"
|
v-for="mode in parameter.modes"
|
||||||
:key="mode.name"
|
:key="mode.name"
|
||||||
:value="mode.name"
|
:value="mode.name"
|
||||||
|
:label="getModeLabel(mode)"
|
||||||
:disabled="isValueExpression && mode.name === 'list'"
|
:disabled="isValueExpression && mode.name === 'list'"
|
||||||
:title="
|
:title="
|
||||||
isValueExpression && mode.name === 'list'
|
isValueExpression && mode.name === 'list'
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ getModeLabel(mode.name) || mode.displayName }}
|
{{ getModeLabel(mode) }}
|
||||||
</n8n-option>
|
</n8n-option>
|
||||||
</n8n-select>
|
</n8n-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -143,8 +144,27 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import type { IResourceLocatorReqParams, IResourceLocatorResultExpanded } from '@/Interface';
|
||||||
import { mapStores } from 'pinia';
|
import DraggableTarget from '@/components/DraggableTarget.vue';
|
||||||
|
import ExpressionParameterInput from '@/components/ExpressionParameterInput.vue';
|
||||||
|
import ParameterIssues from '@/components/ParameterIssues.vue';
|
||||||
|
import { debounceHelper } from '@/mixins/debounce';
|
||||||
|
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
||||||
|
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
||||||
|
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||||
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
import {
|
||||||
|
getAppNameFromNodeName,
|
||||||
|
getMainAuthField,
|
||||||
|
hasOnlyListMode,
|
||||||
|
isResourceLocatorValue,
|
||||||
|
} from '@/utils';
|
||||||
|
import stringify from 'fast-json-stable-stringify';
|
||||||
|
import type { EventBus } from 'n8n-design-system/utils';
|
||||||
|
import { createEventBus } from 'n8n-design-system/utils';
|
||||||
import type {
|
import type {
|
||||||
ILoadOptions,
|
ILoadOptions,
|
||||||
INode,
|
INode,
|
||||||
|
@ -156,29 +176,10 @@ import type {
|
||||||
INodePropertyMode,
|
INodePropertyMode,
|
||||||
NodeParameterValue,
|
NodeParameterValue,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import ExpressionParameterInput from '@/components/ExpressionParameterInput.vue';
|
import { mapStores } from 'pinia';
|
||||||
import DraggableTarget from '@/components/DraggableTarget.vue';
|
|
||||||
import ParameterIssues from '@/components/ParameterIssues.vue';
|
|
||||||
import ResourceLocatorDropdown from './ResourceLocatorDropdown.vue';
|
|
||||||
import type { PropType } from 'vue';
|
import type { PropType } from 'vue';
|
||||||
import type { IResourceLocatorReqParams, IResourceLocatorResultExpanded } from '@/Interface';
|
import { defineComponent } from 'vue';
|
||||||
import { debounceHelper } from '@/mixins/debounce';
|
import ResourceLocatorDropdown from './ResourceLocatorDropdown.vue';
|
||||||
import stringify from 'fast-json-stable-stringify';
|
|
||||||
import { workflowHelpers } from '@/mixins/workflowHelpers';
|
|
||||||
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
|
||||||
import {
|
|
||||||
getAppNameFromNodeName,
|
|
||||||
isResourceLocatorValue,
|
|
||||||
hasOnlyListMode,
|
|
||||||
getMainAuthField,
|
|
||||||
} from '@/utils';
|
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
|
||||||
import { createEventBus } from 'n8n-design-system/utils';
|
|
||||||
import type { EventBus } from 'n8n-design-system/utils';
|
|
||||||
|
|
||||||
interface IResourceLocatorQuery {
|
interface IResourceLocatorQuery {
|
||||||
results: INodeListSearchItems[];
|
results: INodeListSearchItems[];
|
||||||
|
@ -573,12 +574,12 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
getModeLabel(name: string): string | null {
|
getModeLabel(mode: INodePropertyMode): string | null {
|
||||||
if (name === 'id' || name === 'url' || name === 'list') {
|
if (mode.name === 'id' || mode.name === 'url' || mode.name === 'list') {
|
||||||
return this.$locale.baseText(`resourceLocator.mode.${name}`);
|
return this.$locale.baseText(`resourceLocator.mode.${mode.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return mode.displayName;
|
||||||
},
|
},
|
||||||
onInputChange(value: string): void {
|
onInputChange(value: string): void {
|
||||||
const params: INodeParameterResourceLocator = { __rl: true, value, mode: this.selectedMode };
|
const params: INodeParameterResourceLocator = { __rl: true, value, mode: this.selectedMode };
|
||||||
|
|
|
@ -1,70 +1,70 @@
|
||||||
import { defineComponent } from 'vue';
|
|
||||||
import { mapStores } from 'pinia';
|
|
||||||
import {
|
import {
|
||||||
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
|
||||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
|
||||||
WEBHOOK_NODE_TYPE,
|
|
||||||
VIEWS,
|
|
||||||
EnterpriseEditionFeature,
|
EnterpriseEditionFeature,
|
||||||
MODAL_CONFIRM,
|
MODAL_CONFIRM,
|
||||||
|
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||||
|
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
||||||
|
VIEWS,
|
||||||
|
WEBHOOK_NODE_TYPE,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
import { mapStores } from 'pinia';
|
||||||
|
import { defineComponent } from 'vue';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
IConnections,
|
IConnections,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
IExecuteData,
|
||||||
INode,
|
INode,
|
||||||
|
INodeConnection,
|
||||||
|
INodeCredentials,
|
||||||
INodeExecutionData,
|
INodeExecutionData,
|
||||||
INodeIssues,
|
INodeIssues,
|
||||||
INodeParameters,
|
INodeParameters,
|
||||||
NodeParameterValue,
|
INodeProperties,
|
||||||
INodeCredentials,
|
|
||||||
INodeType,
|
INodeType,
|
||||||
INodeTypes,
|
INodeTypes,
|
||||||
IRunExecutionData,
|
IRunExecutionData,
|
||||||
IWorkflowIssues,
|
|
||||||
IWorkflowDataProxyAdditionalKeys,
|
|
||||||
Workflow,
|
|
||||||
IExecuteData,
|
|
||||||
INodeConnection,
|
|
||||||
IWebhookDescription,
|
IWebhookDescription,
|
||||||
INodeProperties,
|
IWorkflowDataProxyAdditionalKeys,
|
||||||
|
IWorkflowIssues,
|
||||||
IWorkflowSettings,
|
IWorkflowSettings,
|
||||||
|
NodeParameterValue,
|
||||||
|
Workflow,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { NodeConnectionType, ExpressionEvaluatorProxy, NodeHelpers } from 'n8n-workflow';
|
import { NodeConnectionType, ExpressionEvaluatorProxy, NodeHelpers } from 'n8n-workflow';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
|
ICredentialsResponse,
|
||||||
INodeTypesMaxCount,
|
INodeTypesMaxCount,
|
||||||
INodeUi,
|
INodeUi,
|
||||||
IWorkflowData,
|
|
||||||
IWorkflowDb,
|
|
||||||
IWorkflowDataUpdate,
|
|
||||||
XYPosition,
|
|
||||||
ITag,
|
ITag,
|
||||||
|
IWorkflowData,
|
||||||
|
IWorkflowDataUpdate,
|
||||||
|
IWorkflowDb,
|
||||||
TargetItem,
|
TargetItem,
|
||||||
ICredentialsResponse,
|
XYPosition,
|
||||||
} from '../Interface';
|
} from '../Interface';
|
||||||
|
|
||||||
|
import { useMessage, useToast } from '@/composables';
|
||||||
import { externalHooks } from '@/mixins/externalHooks';
|
import { externalHooks } from '@/mixins/externalHooks';
|
||||||
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
|
||||||
import { genericHelpers } from '@/mixins/genericHelpers';
|
import { genericHelpers } from '@/mixins/genericHelpers';
|
||||||
import { useToast, useMessage } from '@/composables';
|
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
||||||
|
|
||||||
import { isEqual } from 'lodash-es';
|
import { isEqual } from 'lodash-es';
|
||||||
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import type { IPermissions } from '@/permissions';
|
||||||
import { getSourceItems } from '@/utils';
|
import { getWorkflowPermissions } from '@/permissions';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
|
||||||
import { useRootStore } from '@/stores/n8nRoot.store';
|
import { useRootStore } from '@/stores/n8nRoot.store';
|
||||||
import { useNDVStore } from '@/stores/ndv.store';
|
import { useNDVStore } from '@/stores/ndv.store';
|
||||||
import { useTemplatesStore } from '@/stores/templates.store';
|
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
|
import { useTemplatesStore } from '@/stores/templates.store';
|
||||||
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
import { useUsersStore } from '@/stores/users.store';
|
||||||
|
import { useWorkflowsEEStore } from '@/stores/workflows.ee.store';
|
||||||
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
|
import { getSourceItems } from '@/utils';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { getWorkflowPermissions } from '@/permissions';
|
|
||||||
import type { IPermissions } from '@/permissions';
|
|
||||||
|
|
||||||
export function getParentMainInputNode(workflow: Workflow, node: INode): INode {
|
export function getParentMainInputNode(workflow: Workflow, node: INode): INode {
|
||||||
const nodeType = useNodeTypesStore().getNodeType(node.type);
|
const nodeType = useNodeTypesStore().getNodeType(node.type);
|
||||||
|
@ -233,35 +233,26 @@ export function resolveRequiredParameters(
|
||||||
inputBranchIndex?: number;
|
inputBranchIndex?: number;
|
||||||
} = {},
|
} = {},
|
||||||
): IDataObject | null {
|
): IDataObject | null {
|
||||||
const loadOptionsDependsOn = currentParameter?.typeOptions?.loadOptionsDependsOn ?? [];
|
const loadOptionsDependsOn = new Set(currentParameter?.typeOptions?.loadOptionsDependsOn ?? []);
|
||||||
|
|
||||||
const initial: { required: INodeParameters; nonrequired: INodeParameters } = {
|
const resolvedParameters = Object.fromEntries(
|
||||||
required: {},
|
Object.entries(parameters).map(([name, parameter]): [string, IDataObject | null] => {
|
||||||
nonrequired: {},
|
const required = loadOptionsDependsOn.has(name);
|
||||||
};
|
|
||||||
|
|
||||||
const { required, nonrequired }: INodeParameters = Object.keys(parameters).reduce(
|
if (required) {
|
||||||
(accu, name: string) => {
|
return [name, resolveParameter(parameter as NodeParameterValue, opts)];
|
||||||
const required = loadOptionsDependsOn.includes(name);
|
} else {
|
||||||
accu[required ? 'required' : 'nonrequired'][name] = parameters[name];
|
try {
|
||||||
|
return [name, resolveParameter(parameter as NodeParameterValue, opts)];
|
||||||
return accu;
|
} catch (error) {
|
||||||
},
|
// ignore any expressions errors for non required parameters
|
||||||
initial,
|
return [name, null];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const resolvedRequired = resolveParameter(required, opts);
|
return resolvedParameters;
|
||||||
let resolvedNonrequired: IDataObject | null = {};
|
|
||||||
try {
|
|
||||||
resolvedNonrequired = resolveParameter(nonrequired, opts);
|
|
||||||
} catch (e) {
|
|
||||||
// ignore any expression errors for example
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...resolvedRequired,
|
|
||||||
...(resolvedNonrequired ?? {}),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurrentWorkflow(copyData?: boolean): Workflow {
|
function getCurrentWorkflow(copyData?: boolean): Workflow {
|
||||||
|
|
Loading…
Reference in a new issue