fix(editor): Make resource locator work with data tables (no-changelog) (#18899)

This commit is contained in:
Jaakko Husso
2025-08-28 11:38:47 +03:00
committed by GitHub
parent d244b99484
commit ab7998b441
7 changed files with 36 additions and 4 deletions

View File

@@ -6,4 +6,5 @@ export class ResourceLocatorRequestDto extends BaseDynamicParametersRequestDto.e
methodName: z.string(), methodName: z.string(),
filter: z.string().optional(), filter: z.string().optional(),
paginationToken: z.string().optional(), paginationToken: z.string().optional(),
projectId: z.string().optional(),
}) {} }) {}

View File

@@ -10,6 +10,7 @@ import type { INodePropertyOptions, NodeParameterValueType } from 'n8n-workflow'
import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service'; import { DynamicNodeParametersService } from '@/services/dynamic-node-parameters.service';
import { getBase } from '@/workflow-execute-additional-data'; import { getBase } from '@/workflow-execute-additional-data';
import { userHasScopes } from '@/permissions.ee/check-access';
@RestController('/dynamic-node-parameters') @RestController('/dynamic-node-parameters')
export class DynamicNodeParametersController { export class DynamicNodeParametersController {
@@ -70,10 +71,22 @@ export class DynamicNodeParametersController {
credentials, credentials,
currentNodeParameters, currentNodeParameters,
nodeTypeAndVersion, nodeTypeAndVersion,
projectId,
} = payload; } = payload;
const additionalData = await getBase(req.user.id, currentNodeParameters); const additionalData = await getBase(req.user.id, currentNodeParameters);
if (projectId) {
if (await userHasScopes(req.user, ['dataStore:listProject'], false, { projectId })) {
// Project ID is currently only added on the additionalData if the user
// has data store listing permission for that project. We should consider
// turning this into a more general check, but as of now data stores are
// the only nodes with project specific resource locators where we want to ensure
// that only data stores belonging to their respective projects are shown.
additionalData.dataStoreProjectId = projectId;
}
}
return await this.service.getResourceLocatorResults( return await this.service.getResourceLocatorResults(
methodName, methodName,
path, path,

View File

@@ -47,9 +47,10 @@ export class DataStoreProxyService implements DataStoreProxyProvider {
async getDataStoreAggregateProxy( async getDataStoreAggregateProxy(
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
dataStoreProjectId?: string,
): Promise<IDataStoreProjectAggregateService> { ): Promise<IDataStoreProjectAggregateService> {
this.validateRequest(node); this.validateRequest(node);
const projectId = await this.getProjectId(workflow); const projectId = dataStoreProjectId ?? (await this.getProjectId(workflow));
return this.makeAggregateOperations(projectId); return this.makeAggregateOperations(projectId);
} }
@@ -58,9 +59,10 @@ export class DataStoreProxyService implements DataStoreProxyProvider {
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
dataStoreId: string, dataStoreId: string,
dataStoreProjectId?: string,
): Promise<IDataStoreProjectService> { ): Promise<IDataStoreProjectService> {
this.validateRequest(node); this.validateRequest(node);
const projectId = await this.getProjectId(workflow); const projectId = dataStoreProjectId ?? (await this.getProjectId(workflow));
return this.makeDataStoreOperations(projectId, dataStoreId); return this.makeDataStoreOperations(projectId, dataStoreId);
} }

View File

@@ -8,6 +8,7 @@ declare module 'n8n-workflow' {
hooks?: ExecutionLifecycleHooks; hooks?: ExecutionLifecycleHooks;
externalSecretsProxy: ExternalSecretsProxy; externalSecretsProxy: ExternalSecretsProxy;
dataStoreProxyProvider?: DataStoreProxyProvider; dataStoreProxyProvider?: DataStoreProxyProvider;
dataStoreProjectId?: string;
} }
} }

View File

@@ -14,8 +14,17 @@ export function getDataStoreHelperFunctions(
const dataStoreProxyProvider = additionalData.dataStoreProxyProvider; const dataStoreProxyProvider = additionalData.dataStoreProxyProvider;
return { return {
getDataStoreAggregateProxy: async () => getDataStoreAggregateProxy: async () =>
await dataStoreProxyProvider.getDataStoreAggregateProxy(workflow, node), await dataStoreProxyProvider.getDataStoreAggregateProxy(
workflow,
node,
additionalData.dataStoreProjectId,
),
getDataStoreProxy: async (dataStoreId: string) => getDataStoreProxy: async (dataStoreId: string) =>
await dataStoreProxyProvider.getDataStoreProxy(workflow, node, dataStoreId), await dataStoreProxyProvider.getDataStoreProxy(
workflow,
node,
dataStoreId,
additionalData.dataStoreProjectId,
),
}; };
} }

View File

@@ -55,6 +55,7 @@ import {
} from '../../utils/fromAIOverrideUtils'; } from '../../utils/fromAIOverrideUtils';
import { N8nNotice } from '@n8n/design-system'; import { N8nNotice } from '@n8n/design-system';
import { completeExpressionSyntax } from '@/utils/expressions'; import { completeExpressionSyntax } from '@/utils/expressions';
import { useProjectsStore } from '@/stores/projects.store';
/** /**
* Regular expression to check if the error message contains credential-related phrases. * Regular expression to check if the error message contains credential-related phrases.
@@ -144,6 +145,7 @@ const ndvStore = useNDVStore();
const rootStore = useRootStore(); const rootStore = useRootStore();
const uiStore = useUIStore(); const uiStore = useUIStore();
const workflowsStore = useWorkflowsStore(); const workflowsStore = useWorkflowsStore();
const projectsStore = useProjectsStore();
const appName = computed(() => { const appName = computed(() => {
if (!props.node) { if (!props.node) {
@@ -270,6 +272,7 @@ const currentRequestParams = computed(() => {
parameters: props.node?.parameters ?? {}, parameters: props.node?.parameters ?? {},
credentials: props.node?.credentials ?? {}, credentials: props.node?.credentials ?? {},
filter: searchFilter.value, filter: searchFilter.value,
projectId: projectsStore.currentProjectId,
}; };
}); });
@@ -722,6 +725,7 @@ async function loadResources() {
methodName: loadOptionsMethod, methodName: loadOptionsMethod,
currentNodeParameters: resolvedNodeParameters, currentNodeParameters: resolvedNodeParameters,
credentials: props.node.credentials, credentials: props.node.credentials,
projectId: projectsStore.currentProjectId,
}; };
if (params.filter) { if (params.filter) {

View File

@@ -924,11 +924,13 @@ export type DataStoreProxyProvider = {
getDataStoreAggregateProxy( getDataStoreAggregateProxy(
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
dataStoreProjectId?: string,
): Promise<IDataStoreProjectAggregateService>; ): Promise<IDataStoreProjectAggregateService>;
getDataStoreProxy( getDataStoreProxy(
workflow: Workflow, workflow: Workflow,
node: INode, node: INode,
dataStoreId: string, dataStoreId: string,
dataStoreProjectId?: string,
): Promise<IDataStoreProjectService>; ): Promise<IDataStoreProjectService>;
}; };