mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
add filtering by creds
This commit is contained in:
parent
32d3c8aa1a
commit
26877949b9
|
@ -637,6 +637,22 @@ export interface IWorkflowSettings extends IWorkflowSettingsWorkflow {
|
||||||
executionOrder: NonNullable<IWorkflowSettingsWorkflow['executionOrder']>;
|
executionOrder: NonNullable<IWorkflowSettingsWorkflow['executionOrder']>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkflowsFetchOptions {
|
||||||
|
filter?: {
|
||||||
|
tags?: string[];
|
||||||
|
active?: boolean;
|
||||||
|
projectId?: string;
|
||||||
|
};
|
||||||
|
sort?: 'lastUpdated' | 'lastCreated' | 'nameDesc' | 'nameAsc';
|
||||||
|
webhookURL?: string;
|
||||||
|
httpNodeURL?: string;
|
||||||
|
nodeName?: string;
|
||||||
|
nodeTypes?: string[];
|
||||||
|
credentialIds?: string[];
|
||||||
|
skip?: number;
|
||||||
|
take?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ITimeoutHMS {
|
export interface ITimeoutHMS {
|
||||||
hours: number;
|
hours: number;
|
||||||
minutes: number;
|
minutes: number;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import type {
|
||||||
IRestApiContext,
|
IRestApiContext,
|
||||||
IWorkflowDb,
|
IWorkflowDb,
|
||||||
NewWorkflowResponse,
|
NewWorkflowResponse,
|
||||||
|
WorkflowsFetchOptions,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import type {
|
import type {
|
||||||
ExecutionFilters,
|
ExecutionFilters,
|
||||||
|
@ -33,10 +34,11 @@ export async function getWorkflow(context: IRestApiContext, id: string, filter?:
|
||||||
return await makeRestApiRequest<IWorkflowDb>(context, 'GET', `/workflows/${id}`, sendData);
|
return await makeRestApiRequest<IWorkflowDb>(context, 'GET', `/workflows/${id}`, sendData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkflows(context: IRestApiContext, filter?: object) {
|
export async function getWorkflows(context: IRestApiContext, options: WorkflowsFetchOptions) {
|
||||||
return await makeRestApiRequest<IWorkflowDb[]>(context, 'GET', '/workflows', {
|
return await makeRestApiRequest<IWorkflowDb[]>(context, 'GET', '/workflows', {
|
||||||
includeScopes: true,
|
includeScopes: true,
|
||||||
...(filter ? { filter } : {}),
|
...options,
|
||||||
|
credentialIds: options.credentialIds ? options.credentialIds.join(',') : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,10 +87,12 @@ export const useProjectsStore = defineStore(STORES.PROJECTS, () => {
|
||||||
|
|
||||||
const getAllProjects = async () => {
|
const getAllProjects = async () => {
|
||||||
projects.value = await projectsApi.getAllProjects(rootStore.restApiContext);
|
projects.value = await projectsApi.getAllProjects(rootStore.restApiContext);
|
||||||
|
return projects.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMyProjects = async () => {
|
const getMyProjects = async () => {
|
||||||
myProjects.value = await projectsApi.getMyProjects(rootStore.restApiContext);
|
myProjects.value = await projectsApi.getMyProjects(rootStore.restApiContext);
|
||||||
|
return myProjects.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPersonalProject = async () => {
|
const getPersonalProject = async () => {
|
||||||
|
@ -99,9 +101,9 @@ export const useProjectsStore = defineStore(STORES.PROJECTS, () => {
|
||||||
|
|
||||||
const getAvailableProjects = async () => {
|
const getAvailableProjects = async () => {
|
||||||
if (globalProjectPermissions.value.list) {
|
if (globalProjectPermissions.value.list) {
|
||||||
await getAllProjects();
|
return await getAllProjects();
|
||||||
} else {
|
} else {
|
||||||
await getMyProjects();
|
return await getMyProjects();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import type {
|
||||||
WorkflowMetadata,
|
WorkflowMetadata,
|
||||||
IExecutionFlattedResponse,
|
IExecutionFlattedResponse,
|
||||||
IWorkflowTemplateNode,
|
IWorkflowTemplateNode,
|
||||||
|
WorkflowsFetchOptions,
|
||||||
} from '@/Interface';
|
} from '@/Interface';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import type {
|
import type {
|
||||||
|
@ -472,17 +473,10 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, () => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAllWorkflows(projectId?: string): Promise<IWorkflowDb[]> {
|
async function fetchAllWorkflows(filters: WorkflowsFetchOptions): Promise<IWorkflowDb[]> {
|
||||||
const rootStore = useRootStore();
|
const rootStore = useRootStore();
|
||||||
|
|
||||||
const filter = {
|
const workflows = await workflowsApi.getWorkflows(rootStore.restApiContext, filters);
|
||||||
projectId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const workflows = await workflowsApi.getWorkflows(
|
|
||||||
rootStore.restApiContext,
|
|
||||||
isEmpty(filter) ? undefined : filter,
|
|
||||||
);
|
|
||||||
setWorkflows(workflows);
|
setWorkflows(workflows);
|
||||||
return workflows;
|
return workflows;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, watch, ref } from 'vue';
|
import { computed, onMounted, watch, ref, onBeforeMount } from 'vue';
|
||||||
import ResourcesListLayout, { type IResource } from '@/components/layouts/ResourcesListLayout.vue';
|
import ResourcesListLayout, { type IResource } from '@/components/layouts/ResourcesListLayout.vue';
|
||||||
import WorkflowCard from '@/components/WorkflowCard.vue';
|
import WorkflowCard from '@/components/WorkflowCard.vue';
|
||||||
import WorkflowTagsDropdown from '@/components/WorkflowTagsDropdown.vue';
|
import WorkflowTagsDropdown from '@/components/WorkflowTagsDropdown.vue';
|
||||||
import { EnterpriseEditionFeature, MORE_ONBOARDING_OPTIONS_EXPERIMENT, VIEWS } from '@/constants';
|
import { EnterpriseEditionFeature, MORE_ONBOARDING_OPTIONS_EXPERIMENT, VIEWS } from '@/constants';
|
||||||
import type { ITag, IUser, IWorkflowDb } from '@/Interface';
|
import type { ITag, IUser, IWorkflowDb, WorkflowsFetchOptions } from '@/Interface';
|
||||||
import { useUIStore } from '@/stores/ui.store';
|
import { useUIStore } from '@/stores/ui.store';
|
||||||
import { useSettingsStore } from '@/stores/settings.store';
|
import { useSettingsStore } from '@/stores/settings.store';
|
||||||
import { useUsersStore } from '@/stores/users.store';
|
import { useUsersStore } from '@/stores/users.store';
|
||||||
|
@ -34,6 +34,7 @@ import {
|
||||||
import { pickBy } from 'lodash-es';
|
import { pickBy } from 'lodash-es';
|
||||||
import { useCredentialsStore } from '@/stores/credentials.store';
|
import { useCredentialsStore } from '@/stores/credentials.store';
|
||||||
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
import { useNodeTypesStore } from '@/stores/nodeTypes.store';
|
||||||
|
import { ProjectSharingData } from 'n8n-workflow';
|
||||||
|
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
@ -151,33 +152,11 @@ const emptyListDescription = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onFilter = (
|
|
||||||
resource: IWorkflowDb,
|
|
||||||
newFilters: { tags: string[]; search: string; status: string | boolean },
|
|
||||||
matches: boolean,
|
|
||||||
): boolean => {
|
|
||||||
if (settingsStore.areTagsEnabled && newFilters.tags.length > 0) {
|
|
||||||
matches =
|
|
||||||
matches &&
|
|
||||||
newFilters.tags.every((tag) =>
|
|
||||||
(resource.tags as ITag[])?.find((resourceTag) =>
|
|
||||||
typeof resourceTag === 'object'
|
|
||||||
? `${resourceTag.id}` === `${tag}`
|
|
||||||
: `${resourceTag}` === `${tag}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newFilters.status !== '') {
|
|
||||||
matches = matches && resource.active === newFilters.status;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const onFiltersUpdated = (newFilters: Filters) => {
|
const onFiltersUpdated = (newFilters: Filters) => {
|
||||||
Object.assign(filters.value, newFilters);
|
Object.assign(filters.value, newFilters);
|
||||||
|
|
||||||
|
fetchWorkflowsWithFilters();
|
||||||
};
|
};
|
||||||
|
|
||||||
const addWorkflow = () => {
|
const addWorkflow = () => {
|
||||||
|
@ -211,13 +190,28 @@ const trackCategoryLinkClick = (category: string) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchWorkflowsWithFilters = async () => {
|
||||||
|
const { homeProject, status, credentials } = filters.value;
|
||||||
|
const options: WorkflowsFetchOptions = {
|
||||||
|
filter: {
|
||||||
|
projectId: homeProject ? homeProject : undefined,
|
||||||
|
active: status === StatusFilter.ACTIVE ? true : undefined,
|
||||||
|
},
|
||||||
|
credentialIds: credentials.length ? credentials : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('fetching', options);
|
||||||
|
await workflowsStore.fetchAllWorkflows(options);
|
||||||
|
};
|
||||||
|
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
await setFiltersFromQueryString();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
nodeTypesStore.loadNodeTypesIfNotLoaded(),
|
nodeTypesStore.loadNodeTypesIfNotLoaded(),
|
||||||
credentialsStore.fetchAllCredentials(route?.params?.projectId as string | undefined),
|
credentialsStore.fetchAllCredentials(route?.params?.projectId as string | undefined),
|
||||||
usersStore.fetchUsers(),
|
usersStore.fetchUsers(),
|
||||||
workflowsStore.fetchAllWorkflows(route.params?.projectId as string | undefined),
|
fetchWorkflowsWithFilters(),
|
||||||
workflowsStore.fetchActiveWorkflows(),
|
workflowsStore.fetchActiveWorkflows(),
|
||||||
]);
|
]);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
@ -253,8 +247,8 @@ const saveFiltersOnQueryString = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function isValidProjectId(projectId: string) {
|
function isValidProjectId(availableProjects: ProjectSharingData[], projectId: string) {
|
||||||
return projectsStore.availableProjects.some((project) => project.id === projectId);
|
return availableProjects.some((project) => project.id === projectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const setFiltersFromQueryString = async () => {
|
const setFiltersFromQueryString = async () => {
|
||||||
|
@ -263,8 +257,10 @@ const setFiltersFromQueryString = async () => {
|
||||||
const filtersToApply: { [key: string]: string | string[] | boolean } = {};
|
const filtersToApply: { [key: string]: string | string[] | boolean } = {};
|
||||||
|
|
||||||
if (homeProject && typeof homeProject === 'string') {
|
if (homeProject && typeof homeProject === 'string') {
|
||||||
await projectsStore.getAvailableProjects();
|
const available = await projectsStore.getAvailableProjects();
|
||||||
if (isValidProjectId(homeProject)) {
|
console.log('setting home project', homeProject);
|
||||||
|
if (isValidProjectId(available, homeProject)) {
|
||||||
|
console.log('valid home project', homeProject);
|
||||||
filtersToApply.homeProject = homeProject;
|
filtersToApply.homeProject = homeProject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,9 +303,10 @@ watch(
|
||||||
async () => await initialize(),
|
async () => await initialize(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
onBeforeMount(async () => {});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
documentTitle.set(i18n.baseText('workflows.heading'));
|
documentTitle.set(i18n.baseText('workflows.heading'));
|
||||||
await setFiltersFromQueryString();
|
|
||||||
void usersStore.showPersonalizationSurvey();
|
void usersStore.showPersonalizationSurvey();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -319,7 +316,6 @@ onMounted(async () => {
|
||||||
resource-key="workflows"
|
resource-key="workflows"
|
||||||
:resources="allWorkflows"
|
:resources="allWorkflows"
|
||||||
:filters="filters"
|
:filters="filters"
|
||||||
:additional-filters-handler="onFilter"
|
|
||||||
:type-props="{ itemSize: 80 }"
|
:type-props="{ itemSize: 80 }"
|
||||||
:shareable="isShareable"
|
:shareable="isShareable"
|
||||||
:initialize="initialize"
|
:initialize="initialize"
|
||||||
|
|
Loading…
Reference in a new issue