refactor(editor): Extract users api into rest-api-client package (no-changelog) (#18046)

This commit is contained in:
Alex Grozav
2025-08-07 12:01:10 +03:00
committed by GitHub
parent 743c120880
commit 500d07d813
38 changed files with 134 additions and 123 deletions

View File

@@ -17,6 +17,7 @@ export * from './sso';
export * from './tags';
export * from './templates';
export * from './ui';
export * from './users';
export * from './versions';
export * from './webhooks';
export * from './workflowHistory';

View File

@@ -1,20 +1,88 @@
import type {
LoginRequestDto,
PasswordUpdateRequestDto,
Role,
SettingsUpdateRequestDto,
UsersList,
UsersListFilterDto,
UserUpdateRequestDto,
Role,
UsersList,
User,
} from '@n8n/api-types';
import type { Scope } from '@n8n/permissions';
import type {
CurrentUserResponse,
IPersonalizationLatestVersion,
IUserResponse,
} from '@/Interface';
import type { IRestApiContext } from '@n8n/rest-api-client';
import type { IDataObject, IUserSettings } from 'n8n-workflow';
import { makeRestApiRequest } from '@n8n/rest-api-client';
FeatureFlags,
IDataObject,
IPersonalizationSurveyAnswersV4,
IUserSettings,
} from 'n8n-workflow';
import type { IRestApiContext } from '../types';
import { makeRestApiRequest } from '../utils';
export type IPersonalizationSurveyAnswersV1 = {
codingSkill?: string | null;
companyIndustry?: string[] | null;
companySize?: string | null;
otherCompanyIndustry?: string | null;
otherWorkArea?: string | null;
workArea?: string[] | string | null;
};
export type IPersonalizationSurveyAnswersV2 = {
version: 'v2';
automationGoal?: string | null;
codingSkill?: string | null;
companyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
customerType?: string | null;
mspFocus?: string[] | null;
mspFocusOther?: string | null;
otherAutomationGoal?: string | null;
otherCompanyIndustryExtended?: string[] | null;
};
export type IPersonalizationSurveyAnswersV3 = {
version: 'v3';
automationGoal?: string | null;
otherAutomationGoal?: string | null;
companyIndustryExtended?: string[] | null;
otherCompanyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
automationGoalSm?: string[] | null;
automationGoalSmOther?: string | null;
usageModes?: string[] | null;
email?: string | null;
};
export type IPersonalizationLatestVersion = IPersonalizationSurveyAnswersV4;
export type IPersonalizationSurveyVersions =
| IPersonalizationSurveyAnswersV1
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4;
export interface IUserResponse extends User {
globalScopes?: Scope[];
personalizationAnswers?: IPersonalizationSurveyVersions | null;
settings?: IUserSettings | null;
}
export interface CurrentUserResponse extends IUserResponse {
featureFlags?: FeatureFlags;
}
export interface IUser extends IUserResponse {
isDefaultUser: boolean;
isPendingUser: boolean;
inviteAcceptUrl?: string;
fullName?: string;
createdAt?: string;
mfaEnabled: boolean;
mfaAuthenticated?: boolean;
}
export async function loginCurrentUser(
context: IRestApiContext,

View File

@@ -8,7 +8,6 @@ import type {
IVersionNotificationSettings,
ROLE,
Role,
User,
} from '@n8n/api-types';
import type { Scope } from '@n8n/permissions';
import type { NodeCreatorTag } from '@n8n/design-system';
@@ -37,12 +36,10 @@ import type {
ExecutionStatus,
ITelemetryTrackProperties,
WorkflowSettings,
IUserSettings,
INodeExecutionData,
INodeProperties,
NodeConnectionType,
StartNodeData,
IPersonalizationSurveyAnswersV4,
AnnotationVote,
ITaskData,
ISourceData,
@@ -70,7 +67,8 @@ import type { BulkCommand, Undoable } from '@/models/history';
import type { ProjectSharingData } from '@/types/projects.types';
import type { PathItem } from '@n8n/design-system/components/N8nBreadcrumbs/Breadcrumbs.vue';
import { type IconName } from '@n8n/design-system/src/components/N8nIcon/icons';
import type { IconName } from '@n8n/design-system/src/components/N8nIcon/icons';
import type { IUser, IUserResponse } from '@n8n/rest-api-client/api/users';
export * from '@n8n/design-system/types';
@@ -531,73 +529,8 @@ export interface IExecutionDeleteFilter {
ids?: string[];
}
export type IPersonalizationSurveyAnswersV1 = {
codingSkill?: string | null;
companyIndustry?: string[] | null;
companySize?: string | null;
otherCompanyIndustry?: string | null;
otherWorkArea?: string | null;
workArea?: string[] | string | null;
};
export type IPersonalizationSurveyAnswersV2 = {
version: 'v2';
automationGoal?: string | null;
codingSkill?: string | null;
companyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
customerType?: string | null;
mspFocus?: string[] | null;
mspFocusOther?: string | null;
otherAutomationGoal?: string | null;
otherCompanyIndustryExtended?: string[] | null;
};
export type IPersonalizationSurveyAnswersV3 = {
version: 'v3';
automationGoal?: string | null;
otherAutomationGoal?: string | null;
companyIndustryExtended?: string[] | null;
otherCompanyIndustryExtended?: string[] | null;
companySize?: string | null;
companyType?: string | null;
automationGoalSm?: string[] | null;
automationGoalSmOther?: string | null;
usageModes?: string[] | null;
email?: string | null;
};
export type IPersonalizationLatestVersion = IPersonalizationSurveyAnswersV4;
export type IPersonalizationSurveyVersions =
| IPersonalizationSurveyAnswersV1
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4;
export type InvitableRoleName = (typeof ROLE)['Member' | 'Admin'];
export interface IUserResponse extends User {
globalScopes?: Scope[];
personalizationAnswers?: IPersonalizationSurveyVersions | null;
settings?: IUserSettings | null;
}
export interface CurrentUserResponse extends IUserResponse {
featureFlags?: FeatureFlags;
}
export interface IUser extends IUserResponse {
isDefaultUser: boolean;
isPendingUser: boolean;
inviteAcceptUrl?: string;
fullName?: string;
createdAt?: string;
mfaEnabled: boolean;
mfaAuthenticated?: boolean;
}
export interface IUserListAction {
label: string;
value: string;

View File

@@ -1,5 +1,5 @@
import { faker } from '@faker-js/faker';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { SignInType } from '@/constants';
export const createUser = (overrides?: Partial<IUser>): IUser => ({

View File

@@ -1,7 +1,7 @@
import { Factory } from 'miragejs';
import { faker } from '@faker-js/faker';
import { SignInType } from '@/constants';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
export const userFactory = Factory.extend<IUser>({
id(i: number) {

View File

@@ -1,4 +1,4 @@
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { Model } from 'miragejs';
import type { ModelDefinition } from 'miragejs/-types';

View File

@@ -1,4 +1,5 @@
import type { CurrentUserResponse, IInviteResponse, InvitableRoleName } from '@/Interface';
import type { IInviteResponse, InvitableRoleName } from '@/Interface';
import type { CurrentUserResponse } from '@n8n/rest-api-client/api/users';
import type { IRestApiContext } from '@n8n/rest-api-client';
import type { IDataObject } from 'n8n-workflow';
import { makeRestApiRequest } from '@n8n/rest-api-client';

View File

@@ -1,4 +1,4 @@
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { post } from '@n8n/rest-api-client';
const N8N_API_BASE_URL = 'https://api.n8n.io/api';

View File

@@ -3,7 +3,7 @@ import { mock } from 'vitest-mock-extended';
import { STORES } from '@n8n/stores';
import CollaborationPane from '@/components/MainHeader/CollaborationPane.vue';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import type { RenderOptions } from '@/__tests__/render';
import { createComponentRenderer } from '@/__tests__/render';

View File

@@ -6,9 +6,9 @@ import type {
FormFieldValueUpdate,
IFormInputs,
IInviteResponse,
IUser,
InvitableRoleName,
} from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { EnterpriseEditionFeature, VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { useUsersStore } from '@/stores/users.store';

View File

@@ -83,7 +83,8 @@ import {
} from '@/constants';
import { useToast } from '@/composables/useToast';
import Modal from '@/components/Modal.vue';
import type { IFormInputs, IPersonalizationLatestVersion } from '@/Interface';
import type { IFormInputs } from '@/Interface';
import type { IPersonalizationLatestVersion } from '@n8n/rest-api-client/api/users';
import { useRootStore } from '@n8n/stores/useRootStore';
import { useUsersStore } from '@/stores/users.store';
import { createFormEventBus } from '@n8n/design-system/utils';

View File

@@ -6,7 +6,7 @@ import { ROLE, type UsersList } from '@n8n/api-types';
import { type UserAction } from '@n8n/design-system';
import SettingsUsersActionsCell from '@/components/SettingsUsers/SettingsUsersActionsCell.vue';
import { createComponentRenderer } from '@/__tests__/render';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
const baseUser: UsersList['items'][number] = {
id: '1',

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup="">
import type { UsersList } from '@n8n/api-types';
import type { UserAction } from '@n8n/design-system';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
const props = defineProps<{
data: UsersList['items'][number];

View File

@@ -7,7 +7,7 @@ import { type UserAction } from '@n8n/design-system';
import SettingsUsersTable from '@/components/SettingsUsers/SettingsUsersTable.vue';
import { createComponentRenderer } from '@/__tests__/render';
import { useEmitters } from '@/__tests__/utils';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
const { emitters, addEmitter } = useEmitters<
'settingsUsersRoleCell' | 'settingsUsersActionsCell' | 'n8nDataTableServer'

View File

@@ -9,7 +9,7 @@ import {
type ActionDropdownItem,
} from '@n8n/design-system';
import type { TableHeader, TableOptions } from '@n8n/design-system/components/N8nDataTableServer';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import SettingsUsersRoleCell from '@/components/SettingsUsers/SettingsUsersRoleCell.vue';
import SettingsUsersProjectsCell from '@/components/SettingsUsers/SettingsUsersProjectsCell.vue';
import SettingsUsersActionsCell from '@/components/SettingsUsers/SettingsUsersActionsCell.vue';

View File

@@ -3,7 +3,7 @@ import V1Banner from './V1Banner.vue';
import { createPinia, setActivePinia } from 'pinia';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@n8n/api-types';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
const renderComponent = createComponentRenderer(V1Banner, {
global: {

View File

@@ -1,5 +1,5 @@
import { useCalloutHelpers } from '@/composables/useCalloutHelpers';
import { updateCurrentUserSettings } from '@/api/users';
import { updateCurrentUserSettings } from '@n8n/rest-api-client/api/users';
const mocks = vi.hoisted(() => ({
resolve: vi.fn(),
@@ -53,7 +53,7 @@ vi.mock('@n8n/stores/useRootStore', () => ({
}),
}));
vi.mock('@/api/users', () => ({
vi.mock('@n8n/rest-api-client/api/users', () => ({
updateCurrentUserSettings: vi.fn(),
}));

View File

@@ -5,7 +5,7 @@ import { useRootStore } from '@n8n/stores/useRootStore';
import { useUsersStore } from '@/stores/users.store';
import { VIEWS } from '@/constants';
import { getRagStarterWorkflowJson } from '@/utils/easyAiWorkflowUtils';
import { updateCurrentUserSettings } from '@/api/users';
import { updateCurrentUserSettings } from '@n8n/rest-api-client/api/users';
import { useWorkflowsStore } from '@/stores/workflows.store';
export function useCalloutHelpers() {

View File

@@ -10,7 +10,8 @@ import { computed, onMounted, ref } from 'vue';
import { useRoute } from 'vue-router';
import { ProjectTypes } from '@/types/projects.types';
import { useProjectsStore } from '@/stores/projects.store';
import type { IUser, SortingAndPaginationUpdates, UserAction } from '@/Interface';
import type { SortingAndPaginationUpdates, UserAction } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import type { DataStoreResource } from '@/features/dataStore/types';
import DataStoreCard from '@/features/dataStore/components/DataStoreCard.vue';
import { useSourceControlStore } from '@/stores/sourceControl.store';

View File

@@ -2,7 +2,8 @@ import { createComponentRenderer } from '@/__tests__/render';
import DataStoreCard from './DataStoreCard.vue';
import { createPinia, setActivePinia } from 'pinia';
import type { DataStoreResource } from '@/features/dataStore/types';
import type { UserAction, IUser } from '@/Interface';
import type { UserAction } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
vi.mock('vue-router', () => {
const push = vi.fn();

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import type { IUser, UserAction } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import type { UserAction } from '@/Interface';
import type { DataStoreResource } from '@/features/dataStore/types';
import { DATA_STORE_DETAILS } from '../constants';
import { useI18n } from '@n8n/i18n';

View File

@@ -6,7 +6,7 @@ import { useRootStore } from '@n8n/stores/useRootStore';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';
import { mockedStore, type MockedStore } from '@/__tests__/utils';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { reactive } from 'vue';
import type { FrontendModuleSettings } from '@n8n/api-types';

View File

@@ -14,7 +14,7 @@ import { mockedStore, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { STORES } from '@n8n/stores';
import { useSSOStore } from '@/stores/sso.store';
import { UserManagementAuthenticationMethod } from '@/Interface';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { EnterpriseEditionFeature } from '@/constants';
import { useUIStore } from '@/stores/ui.store';
import type { Cloud } from '@n8n/rest-api-client';

View File

@@ -1,4 +1,4 @@
import type { CurrentUserResponse } from '@/Interface';
import type { CurrentUserResponse } from '@n8n/rest-api-client/api/users';
import { useUsersStore } from './users.store';
import { createPinia, setActivePinia } from 'pinia';
@@ -10,7 +10,7 @@ const { loginCurrentUser, inviteUsers } = vi.hoisted(() => {
};
});
vi.mock('@/api/users', () => ({
vi.mock('@n8n/rest-api-client/api/users', () => ({
loginCurrentUser,
}));

View File

@@ -8,18 +8,18 @@ import {
ROLE,
type UsersListFilterDto,
} from '@n8n/api-types';
import type { UpdateGlobalRolePayload } from '@/api/users';
import * as usersApi from '@/api/users';
import type { UpdateGlobalRolePayload } from '@n8n/rest-api-client/api/users';
import * as usersApi from '@n8n/rest-api-client/api/users';
import { BROWSER_ID_STORAGE_KEY } from '@n8n/constants';
import { PERSONALIZATION_MODAL_KEY } from '@/constants';
import { STORES } from '@n8n/stores';
import type { InvitableRoleName } from '@/Interface';
import type { IUserResponse } from '@n8n/rest-api-client/api/users';
import type {
IPersonalizationLatestVersion,
IUser,
IUserResponse,
CurrentUserResponse,
InvitableRoleName,
} from '@/Interface';
IPersonalizationLatestVersion,
} from '@n8n/rest-api-client/api/users';
import { getPersonalizedNodeTypes } from '@/utils/userUtils';
import { defineStore } from 'pinia';
import { useRootStore } from '@n8n/stores/useRootStore';

View File

@@ -99,7 +99,7 @@ import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers';
import { useSettingsStore } from './settings.store';
import { useNodeHelpers } from '@/composables/useNodeHelpers';
import { useUsersStore } from '@/stores/users.store';
import { updateCurrentUserSettings } from '@/api/users';
import { updateCurrentUserSettings } from '@n8n/rest-api-client/api/users';
import { useExecutingNode } from '@/composables/useExecutingNode';
import type { NodeExecuteBefore } from '@n8n/api-types/push/execution';
import { isChatNode } from '@/utils/aiUtils';

View File

@@ -15,10 +15,10 @@ import type {
INodeCreateElement,
INodeUi,
INodeUpdatePropertiesInformation,
IPersonalizationLatestVersion,
IWorkflowDb,
NodeFilterType,
} from '@/Interface';
import type { IPersonalizationLatestVersion } from '@n8n/rest-api-client/api/users';
import type { IWorkflowTemplateNode } from '@n8n/rest-api-client/api/templates';
import type { ComponentPublicInstance } from 'vue';
import type { useWebhooksStore } from '@/stores/webhooks.store';

View File

@@ -1,5 +1,5 @@
import type { Scope, ProjectRole } from '@n8n/permissions';
import type { IUserResponse } from '@/Interface';
import type { IUserResponse } from '@n8n/rest-api-client/api/users';
export const ProjectTypes = {
Personal: 'personal',

View File

@@ -1,6 +1,6 @@
import { useUsersStore } from '@/stores/users.store';
import { isAuthenticated } from '@/utils/rbac/checks/isAuthenticated';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
vi.mock('@/stores/users.store', () => ({
useUsersStore: vi.fn(),

View File

@@ -1,6 +1,6 @@
import { roleMiddleware } from '@/utils/rbac/middleware/role';
import { useUsersStore } from '@/stores/users.store';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import type { RouteLocationNormalized } from 'vue-router';
import { ROLE } from '@n8n/api-types';
import { VIEWS } from '@/constants';

View File

@@ -67,8 +67,8 @@ import type {
IPersonalizationSurveyAnswersV3,
IPersonalizationSurveyVersions,
IUser,
ILogInStatus,
} from '@/Interface';
} from '@n8n/rest-api-client/api/users';
import type { ILogInStatus } from '@/Interface';
import type { IPersonalizationSurveyAnswersV4 } from 'n8n-workflow';
/*

View File

@@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
import { deepCopy } from 'n8n-workflow';
import { N8nFormInput } from '@n8n/design-system';
import { useUsersStore } from '@/stores/users.store';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { useI18n } from '@n8n/i18n';
import { useProjectsStore } from '@/stores/projects.store';
import type { Project, ProjectRelation } from '@/types/projects.types';

View File

@@ -4,7 +4,8 @@ import { ROLE, type Role } from '@n8n/api-types';
import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
import { useDocumentTitle } from '@/composables/useDocumentTitle';
import type { IFormInputs, IUser, ThemeOption } from '@/Interface';
import type { IFormInputs, ThemeOption } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import {
CHANGE_PASSWORD_MODAL_KEY,
MFA_DOCS_URL,

View File

@@ -7,7 +7,7 @@ import SettingsUsageAndPlan from '@/views/SettingsUsageAndPlan.vue';
import { useUIStore } from '@/stores/ui.store';
import { COMMUNITY_PLUS_ENROLLMENT_MODAL } from '@/constants';
import { useUsersStore } from '@/stores/users.store';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
vi.mock('vue-router', () => {
return {

View File

@@ -4,7 +4,7 @@ import { screen, waitFor } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { vi } from 'vitest';
import { type FrontendSettings, type Role, ROLE, type UsersList } from '@n8n/api-types';
import type { IUser } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import {
INVITE_USER_MODAL_KEY,
DELETE_USER_MODAL_KEY,

View File

@@ -15,7 +15,8 @@ import {
EnterpriseEditionFeature,
INVITE_USER_MODAL_KEY,
} from '@/constants';
import type { InvitableRoleName, IUser } from '@/Interface';
import type { InvitableRoleName } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { useToast } from '@/composables/useToast';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';

View File

@@ -11,7 +11,8 @@ import { mockedStore, SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { createRouter, createWebHistory } from 'vue-router';
import userEvent from '@testing-library/user-event';
import { waitFor, within } from '@testing-library/vue';
import type { IUser, EnvironmentVariable } from '@/Interface';
import type { EnvironmentVariable } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import type { Scope } from '@n8n/permissions';
const router = createRouter({

View File

@@ -1,9 +1,10 @@
import { createComponentRenderer } from '@/__tests__/render';
import { mockedStore, waitAllPromises } from '@/__tests__/utils';
import * as usersApi from '@/api/users';
import * as usersApi from '@n8n/rest-api-client/api/users';
import { useProjectPages } from '@/composables/useProjectPages';
import { VIEWS } from '@/constants';
import type { IUser, WorkflowListResource } from '@/Interface';
import type { WorkflowListResource } from '@/Interface';
import type { IUser } from '@n8n/rest-api-client/api/users';
import { useFoldersStore } from '@/stores/folders.store';
import { useProjectsStore } from '@/stores/projects.store';
import { useSettingsStore } from '@/stores/settings.store';
@@ -21,7 +22,7 @@ import { waitFor } from '@testing-library/vue';
import { createRouter, createWebHistory } from 'vue-router';
vi.mock('@/api/projects.api');
vi.mock('@/api/users');
vi.mock('@n8n/rest-api-client/api/users');
vi.mock('@/api/sourceControl');
vi.mock('@/composables/useGlobalEntityCreation', () => ({
useGlobalEntityCreation: () => ({