mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
feat(editor): Add option to disable credentials check in RLC (#17381)
This commit is contained in:
@@ -103,3 +103,34 @@ export const TEST_NODE_SINGLE_MODE: INode = {
|
||||
options: {},
|
||||
},
|
||||
};
|
||||
|
||||
export const TEST_PARAMETER_SKIP_CREDENTIALS_CHECK: INodeProperties = {
|
||||
...TEST_PARAMETER_MULTI_MODE,
|
||||
name: 'testParameterSkipCredentialsCheck',
|
||||
modes: [
|
||||
{
|
||||
displayName: 'From List',
|
||||
name: 'list',
|
||||
type: 'list',
|
||||
typeOptions: {
|
||||
searchListMethod: 'testSearch',
|
||||
searchable: true,
|
||||
skipCredentialsCheckInRLC: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const TEST_NODE_NO_CREDENTIALS: INode = {
|
||||
...TEST_NODE_MULTI_MODE,
|
||||
name: 'Test Node - No Credentials',
|
||||
parameters: {
|
||||
authentication: undefined,
|
||||
resource: 'test',
|
||||
operation: 'get',
|
||||
testParameterSkipCredentialsCheck: TEST_MODEL_VALUE,
|
||||
id: '',
|
||||
options: {},
|
||||
},
|
||||
credentials: undefined,
|
||||
};
|
||||
|
||||
@@ -9,9 +9,11 @@ import {
|
||||
TEST_MODEL_VALUE,
|
||||
TEST_NODE_MULTI_MODE,
|
||||
TEST_NODE_SINGLE_MODE,
|
||||
TEST_NODE_NO_CREDENTIALS,
|
||||
TEST_PARAMETER_ADD_RESOURCE,
|
||||
TEST_PARAMETER_MULTI_MODE,
|
||||
TEST_PARAMETER_SINGLE_MODE,
|
||||
TEST_PARAMETER_SKIP_CREDENTIALS_CHECK,
|
||||
} from './ResourceLocator.test.constants';
|
||||
|
||||
vi.mock('vue-router', async () => {
|
||||
@@ -197,6 +199,101 @@ describe('ResourceLocator', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders error when credentials are required and skipCredentialsCheckInRLC is false', async () => {
|
||||
nodeTypesStore.getResourceLocatorResults.mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
name: 'Test Resource',
|
||||
value: 'test-resource',
|
||||
url: 'https://test.com/test-resource',
|
||||
},
|
||||
],
|
||||
paginationToken: null,
|
||||
});
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue({
|
||||
displayName: 'Test Node',
|
||||
credentials: [
|
||||
{
|
||||
name: 'testAuth',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { getByTestId, queryByTestId } = renderComponent({
|
||||
props: {
|
||||
modelValue: TEST_MODEL_VALUE,
|
||||
parameter: TEST_PARAMETER_MULTI_MODE,
|
||||
path: `parameters.${TEST_PARAMETER_MULTI_MODE.name}`,
|
||||
node: TEST_NODE_NO_CREDENTIALS,
|
||||
displayTitle: 'Test Resource Locator',
|
||||
expressionComputedValue: '',
|
||||
isValueExpression: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(getByTestId(`resource-locator-${TEST_PARAMETER_MULTI_MODE.name}`)).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(getByTestId('rlc-input'));
|
||||
|
||||
expect(getByTestId('rlc-error-container')).toBeInTheDocument();
|
||||
expect(getByTestId('permission-error-link')).toBeInTheDocument();
|
||||
expect(queryByTestId('rlc-item')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders list items when skipCredentialsCheckInRLC is true even without credentials', async () => {
|
||||
const TEST_ITEMS = [
|
||||
{ name: 'Test Resource', value: 'test-resource', url: 'https://test.com/test-resource' },
|
||||
{
|
||||
name: 'Test Resource 2',
|
||||
value: 'test-resource-2',
|
||||
url: 'https://test.com/test-resource-2',
|
||||
},
|
||||
];
|
||||
nodeTypesStore.getResourceLocatorResults.mockResolvedValue({
|
||||
results: TEST_ITEMS,
|
||||
paginationToken: null,
|
||||
});
|
||||
nodeTypesStore.getNodeType = vi.fn().mockReturnValue({
|
||||
displayName: 'Test Node',
|
||||
credentials: [
|
||||
{
|
||||
name: 'testAuth',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { getByTestId, getByText, getAllByTestId, queryByTestId } = renderComponent({
|
||||
props: {
|
||||
modelValue: TEST_MODEL_VALUE,
|
||||
parameter: TEST_PARAMETER_SKIP_CREDENTIALS_CHECK,
|
||||
path: `parameters.${TEST_PARAMETER_SKIP_CREDENTIALS_CHECK.name}`,
|
||||
node: TEST_NODE_NO_CREDENTIALS,
|
||||
displayTitle: 'Test Resource Locator',
|
||||
expressionComputedValue: '',
|
||||
isValueExpression: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
getByTestId(`resource-locator-${TEST_PARAMETER_SKIP_CREDENTIALS_CHECK.name}`),
|
||||
).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(getByTestId('rlc-input'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(nodeTypesStore.getResourceLocatorResults).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(getAllByTestId('rlc-item')).toHaveLength(TEST_ITEMS.length);
|
||||
TEST_ITEMS.forEach((item) => {
|
||||
expect(getByText(item.name)).toBeInTheDocument();
|
||||
});
|
||||
expect(queryByTestId('rlc-error-container')).not.toBeInTheDocument();
|
||||
expect(queryByTestId('permission-error-link')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Testing error message deduplication
|
||||
describe('ResourceLocator credentials error handling', () => {
|
||||
it.each([
|
||||
|
||||
@@ -186,8 +186,9 @@ const hasCredentialError = computed(() => {
|
||||
);
|
||||
});
|
||||
|
||||
const credentialsNotSet = computed(() => {
|
||||
const credentialsRequiredAndNotSet = computed(() => {
|
||||
if (!props.node) return false;
|
||||
if (skipCredentialsCheckInRLC.value) return false;
|
||||
const nodeType = nodeTypesStore.getNodeType(props.node.type);
|
||||
if (nodeType) {
|
||||
const usesCredentials = nodeType.credentials !== undefined && nodeType.credentials.length > 0;
|
||||
@@ -315,6 +316,10 @@ const currentQueryError = computed(() => {
|
||||
|
||||
const isSearchable = computed(() => !!getPropertyArgument(currentMode.value, 'searchable'));
|
||||
|
||||
const skipCredentialsCheckInRLC = computed(
|
||||
() => !!getPropertyArgument(currentMode.value, 'skipCredentialsCheckInRLC'),
|
||||
);
|
||||
|
||||
const requiresSearchFilter = computed(
|
||||
() => !!getPropertyArgument(currentMode.value, 'searchFilterRequired'),
|
||||
);
|
||||
@@ -666,7 +671,7 @@ async function loadResources() {
|
||||
const paramsKey = currentRequestKey.value;
|
||||
const cachedResponse = cachedResponses.value[paramsKey];
|
||||
|
||||
if (credentialsNotSet.value) {
|
||||
if (credentialsRequiredAndNotSet.value) {
|
||||
setResponse(paramsKey, { error: true });
|
||||
return;
|
||||
}
|
||||
@@ -922,7 +927,7 @@ function removeOverride() {
|
||||
<template #error>
|
||||
<div :class="$style.errorContainer" data-test-id="rlc-error-container">
|
||||
<n8n-text
|
||||
v-if="credentialsNotSet || currentResponse.errorDetails"
|
||||
v-if="credentialsRequiredAndNotSet || currentResponse.errorDetails"
|
||||
color="text-dark"
|
||||
align="center"
|
||||
tag="div"
|
||||
@@ -946,9 +951,12 @@ function removeOverride() {
|
||||
{{ currentResponse.errorDetails.description }}
|
||||
</N8nNotice>
|
||||
</div>
|
||||
<div v-if="hasCredentialError || credentialsNotSet" data-test-id="permission-error-link">
|
||||
<div
|
||||
v-if="hasCredentialError || credentialsRequiredAndNotSet"
|
||||
data-test-id="permission-error-link"
|
||||
>
|
||||
<a
|
||||
v-if="credentialsNotSet"
|
||||
v-if="credentialsRequiredAndNotSet"
|
||||
:class="$style['credential-link']"
|
||||
@click="createNewCredential"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user