fix: Expose projectId for remaining Data Table contexts (no-changelog) (#19058)

This commit is contained in:
Charlie Kolb
2025-09-01 16:15:53 +02:00
committed by GitHub
parent 73cc042ebc
commit 81c26676a3
6 changed files with 39 additions and 3 deletions

View File

@@ -15,4 +15,5 @@ export class BaseDynamicParametersRequestDto extends Z.class({
credentials: z.record(z.string(), z.any()).optional() satisfies z.ZodType<
INodeCredentials | undefined
>,
projectId: z.string().optional(),
}) {}

View File

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

View File

@@ -29,10 +29,22 @@ export class DynamicNodeParametersController {
path,
methodName,
loadOptions,
projectId,
} = payload;
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;
}
}
if (methodName) {
return await this.service.getOptionsViaMethodName(
methodName,
@@ -105,10 +117,22 @@ export class DynamicNodeParametersController {
_res: Response,
@Body payload: ResourceMapperFieldsRequestDto,
) {
const { path, methodName, credentials, currentNodeParameters, nodeTypeAndVersion } = payload;
const { path, methodName, credentials, currentNodeParameters, nodeTypeAndVersion, projectId } =
payload;
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.getResourceMappingFields(
methodName,
path,

View File

@@ -87,6 +87,7 @@ import CssEditor from './CssEditor/CssEditor.vue';
import { useFocusPanelStore } from '@/stores/focusPanel.store';
import ExperimentalEmbeddedNdvMapper from '@/components/canvas/experimental/components/ExperimentalEmbeddedNdvMapper.vue';
import { useExperimentalNdvStore } from '@/components/canvas/experimental/experimentalNdv.store';
import { useProjectsStore } from '@/stores/projects.store';
type Picker = { $emit: (arg0: string, arg1: Date) => void };
@@ -152,6 +153,7 @@ const nodeTypesStore = useNodeTypesStore();
const uiStore = useUIStore();
const focusPanelStore = useFocusPanelStore();
const experimentalNdvStore = useExperimentalNdvStore();
const projectsStore = useProjectsStore();
const expressionLocalResolveCtx = inject(ExpressionLocalResolveContextSymbol, undefined);
@@ -688,6 +690,7 @@ async function loadRemoteParameterOptions() {
loadOptions,
currentNodeParameters: resolvedNodeParameters,
credentials: node.value.credentials,
projectId: projectsStore.currentProjectId,
});
remoteParameterOptions.value = remoteParameterOptions.value.concat(options);

View File

@@ -4,13 +4,17 @@ import {
UPDATED_SCHEMA,
} from './__tests__/utils/ResourceMapper.utils';
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
import { cleanupAppModals, createAppModals, waitAllPromises } from '@/__tests__/utils';
import type { MockedStore } from '@/__tests__/utils';
import { cleanupAppModals, createAppModals, mockedStore, waitAllPromises } from '@/__tests__/utils';
import ResourceMapper from '@/components/ResourceMapper/ResourceMapper.vue';
import userEvent from '@testing-library/user-event';
import { createComponentRenderer } from '@/__tests__/render';
import type { MockInstance } from 'vitest';
import { useProjectsStore } from '@/stores/projects.store';
let nodeTypeStore: ReturnType<typeof useNodeTypesStore>;
let projectsStore: MockedStore<typeof useProjectsStore>;
let fetchFieldsSpy: MockInstance;
const renderComponent = createComponentRenderer(ResourceMapper, DEFAULT_SETUP);
@@ -25,6 +29,8 @@ describe('ResourceMapper.vue', () => {
beforeEach(() => {
createAppModals();
projectsStore = mockedStore(useProjectsStore);
projectsStore.currentProjectId = 'aProjectId';
});
afterEach(() => {

View File

@@ -30,6 +30,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { useDocumentVisibility } from '@/composables/useDocumentVisibility';
import { N8nButton, N8nCallout, N8nNotice } from '@n8n/design-system';
import isEqual from 'lodash/isEqual';
import { useProjectsStore } from '@/stores/projects.store';
type Props = {
parameter: INodeProperties;
@@ -46,6 +47,7 @@ type Props = {
const nodeTypesStore = useNodeTypesStore();
const ndvStore = useNDVStore();
const workflowsStore = useWorkflowsStore();
const projectsStore = useProjectsStore();
const props = withDefaults(defineProps<Props>(), {
teleported: true,
@@ -310,6 +312,7 @@ const createRequestParams = (methodName: string) => {
path: props.path,
methodName,
credentials: props.node.credentials,
projectId: projectsStore.currentProjectId,
};
return requestParams;