Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into fix-gh-8898
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy committed Mar 18, 2024
2 parents 8041ca3 + 6955e89 commit d3f5cfb
Show file tree
Hide file tree
Showing 26 changed files with 87 additions and 67 deletions.
12 changes: 11 additions & 1 deletion packages/@n8n/chat/src/plugins/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,19 @@ export const ChatPlugin: Plugin<ChatOptions> = {
options,
);

let textMessage = sendMessageResponse.output ?? sendMessageResponse.text ?? '';

if (textMessage === '' && Object.keys(sendMessageResponse).length > 0) {
try {
textMessage = JSON.stringify(sendMessageResponse, null, 2);
} catch (e) {
// Failed to stringify the object so fallback to empty string
}
}

const receivedMessage: ChatMessage = {
id: uuidv4(),
text: sendMessageResponse.output,
text: textMessage,
sender: 'bot',
createdAt: new Date().toISOString(),
};
Expand Down
3 changes: 2 additions & 1 deletion packages/@n8n/chat/src/types/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export interface LoadPreviousSessionResponse {
}

export interface SendMessageResponse {
output: string;
output?: string;
text?: string;
}
10 changes: 5 additions & 5 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type {
REGULAR_NODE_CREATOR_VIEW,
AI_OTHERS_NODE_CREATOR_VIEW,
VIEWS,
} from './constants';
ROLE,
} from '@/constants';
import type { IMenuItem } from 'n8n-design-system';
import {
type GenericValue,
Expand Down Expand Up @@ -688,9 +689,9 @@ export type IPersonalizationSurveyVersions =
| IPersonalizationSurveyAnswersV2
| IPersonalizationSurveyAnswersV3;

export type IRole = 'default' | 'global:owner' | 'global:member' | 'global:admin';

export type InvitableRoleName = 'global:member' | 'global:admin';
export type Roles = typeof ROLE;
export type IRole = Roles[keyof Roles];
export type InvitableRoleName = Roles['Member' | 'Admin'];

export interface IUserResponse {
id: string;
Expand All @@ -714,7 +715,6 @@ export interface IUser extends IUserResponse {
isDefaultUser: boolean;
isPendingUser: boolean;
hasRecoveryCodesLeft: boolean;
isOwner: boolean;
inviteAcceptUrl?: string;
fullName?: string;
createdAt?: string;
Expand Down
5 changes: 4 additions & 1 deletion packages/editor-ui/src/__tests__/permissions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { parsePermissionsTable } from '@/permissions';
import type { IUser } from '@/Interface';
import { ROLE } from '@/constants';

describe('parsePermissionsTable()', () => {
const user: IUser = {
id: '1',
firstName: 'John',
lastName: 'Doe',
isDefaultUser: false,
isOwner: true,
isPending: false,
isPendingUser: false,
mfaEnabled: false,
hasRecoveryCodesLeft: false,
role: ROLE.Owner,
};

it('should return permissions object using generic permissions table', () => {
Expand Down
9 changes: 6 additions & 3 deletions packages/editor-ui/src/__tests__/server/factories/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ export const userFactory = Factory.extend<IUser>({
isDefaultUser() {
return false;
},
isOwner() {
return false;
},
isPending() {
return false;
},
Expand All @@ -28,4 +25,10 @@ export const userFactory = Factory.extend<IUser>({
signInType(): SignInType {
return SignInType.EMAIL;
},
mfaEnabled() {
return false;
},
hasRecoveryCodesLeft() {
return false;
},
});
4 changes: 2 additions & 2 deletions packages/editor-ui/src/api/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type {
CurrentUserResponse,
IPersonalizationLatestVersion,
IRestApiContext,
IRole,
IUserResponse,
InvitableRoleName,
} from '@/Interface';
import type { IDataObject } from 'n8n-workflow';
import { makeRestApiRequest } from '@/utils/apiUtils';
Expand Down Expand Up @@ -157,7 +157,7 @@ export async function submitPersonalizationSurvey(

export interface UpdateGlobalRolePayload {
id: string;
newRoleName: Exclude<IRole, 'default' | 'global:owner'>;
newRoleName: InvitableRoleName;
}

export async function updateGlobalRole(
Expand Down
3 changes: 2 additions & 1 deletion packages/editor-ui/src/api/workflow-webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { IOnboardingCallPrompt, IUser } from '@/Interface';
import { get, post } from '@/utils/apiUtils';
import { isUserGlobalOwner } from '@/utils/userUtils';

const N8N_API_BASE_URL = 'https://api.n8n.io/api';
const ONBOARDING_PROMPTS_ENDPOINT = '/prompts/onboarding';
Expand All @@ -12,7 +13,7 @@ export async function fetchNextOnboardingPrompt(
return await get(N8N_API_BASE_URL, ONBOARDING_PROMPTS_ENDPOINT, {
instance_id: instanceId,
user_id: `${instanceId}#${currentUser.id}`,
is_owner: currentUser.isOwner ?? false,
is_owner: isUserGlobalOwner(currentUser),
survey_results: currentUser.personalizationAnswers,
});
}
Expand Down
8 changes: 6 additions & 2 deletions packages/editor-ui/src/components/InviteUsersModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ import { mapStores } from 'pinia';
import { useToast } from '@/composables/useToast';
import Modal from './Modal.vue';
import type { IFormInputs, IInviteResponse, IUser } from '@/Interface';
import { ROLE } from '@/utils/userUtils';
import { EnterpriseEditionFeature, VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from '@/constants';
import {
EnterpriseEditionFeature,
VALID_EMAIL_REGEX,
INVITE_USER_MODAL_KEY,
ROLE,
} from '@/constants';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useUIStore } from '@/stores/ui.store';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store';
import { useCollaborationStore } from '@/stores/collaboration.store';
import { onBeforeUnmount, onMounted, computed, ref } from 'vue';
import { TIME } from '@/constants';
import { isUserGlobalOwner } from '@/utils/userUtils';
const collaborationStore = useCollaborationStore();
const usersStore = useUsersStore();
Expand All @@ -16,7 +17,7 @@ const activeUsersSorted = computed(() => {
const currentWorkflowUsers = (collaborationStore.getUsersForCurrentWorkflow ?? []).map(
(userInfo) => userInfo.user,
);
const owner = currentWorkflowUsers.find((user) => user.role === 'global:owner');
const owner = currentWorkflowUsers.find(isUserGlobalOwner);
return {
defaultGroup: owner
? [owner, ...currentWorkflowUsers.filter((user) => user.id !== owner.id)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { merge } from 'lodash-es';
import userEvent from '@testing-library/user-event';

import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { STORES } from '@/constants';
import { ROLE, STORES } from '@/constants';

import { createTestingPinia } from '@pinia/testing';
import BannerStack from '@/components/banners/BannerStack.vue';
Expand All @@ -26,11 +26,11 @@ const initialState = {
users: {
'aaa-bbb': {
id: 'aaa-bbb',
role: 'global:owner',
role: ROLE.Owner,
},
'bbb-bbb': {
id: 'bbb-bbb',
role: 'global:member',
role: ROLE.Member,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { merge } from 'lodash-es';
import { SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils';
import { STORES } from '@/constants';
import { ROLE, STORES } from '@/constants';
import { createTestingPinia } from '@pinia/testing';
import { useUIStore } from '@/stores/ui.store';
import CollaborationPane from '@/components//MainHeader/CollaborationPane.vue';
Expand All @@ -13,10 +13,9 @@ const OWNER_USER = {
email: '[email protected]',
firstName: 'Owner',
lastName: 'User',
role: 'global:owner',
role: ROLE.Owner,
disabled: false,
isPending: false,
isOwner: true,
fullName: 'Owner User',
};

Expand All @@ -26,10 +25,9 @@ const MEMBER_USER = {
email: '[email protected]',
firstName: 'Member',
lastName: 'User',
role: 'global:member',
role: ROLE.Member,
disabled: false,
isPending: false,
isOwner: false,
fullName: 'Member User',
};

Expand All @@ -39,10 +37,9 @@ const MEMBER_USER_2 = {
email: '[email protected]',
firstName: 'Another Member',
lastName: 'User',
role: 'global:member',
role: ROLE.Member,
disabled: false,
isPending: false,
isOwner: false,
fullName: 'Another Member User',
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import PersonalizationModal from '@/components/PersonalizationModal.vue';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { PERSONALIZATION_MODAL_KEY, STORES, VIEWS } from '@/constants';
import { PERSONALIZATION_MODAL_KEY, ROLE, STORES, VIEWS } from '@/constants';
import { retry } from '@/__tests__/utils';
import { createComponentRenderer } from '@/__tests__/render';
import { fireEvent } from '@testing-library/vue';
Expand Down Expand Up @@ -31,7 +31,7 @@ const pinia = createTestingPinia({
isDefaultUser: false,
isPendingUser: false,
hasRecoveryCodesLeft: true,
isOwner: true,
role: ROLE.Owner,
mfaEnabled: false,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { render } from '@testing-library/vue';
import V1Banner from '../V1Banner.vue';
import { createPinia, setActivePinia } from 'pinia';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@/constants';
import type { IUser } from '@/Interface';

describe('V1 Banner', () => {
let pinia: ReturnType<typeof createPinia>;
Expand All @@ -22,8 +24,8 @@ describe('V1 Banner', () => {

it('should render banner with dismiss call if user is owner', () => {
vi.spyOn(usersStore, 'currentUser', 'get').mockReturnValue({
role: 'global:owner',
});
role: ROLE.Owner,
} as IUser);

const { container } = render(V1Banner);
expect(container).toMatchSnapshot();
Expand Down
7 changes: 7 additions & 0 deletions packages/editor-ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,13 @@ export const TEMPLATES_URLS = {
UTM_QUERY: 'utm_source=n8n_app&utm_medium=template_library',
};

export const ROLE = {
Owner: 'global:owner',
Member: 'global:member',
Admin: 'global:admin',
Default: 'default', // default user with no email when setting up instance
} as const;

export const INSECURE_CONNECTION_WARNING = `
<body style="margin-top: 20px; font-family: 'Open Sans', sans-serif; text-align: center;">
<h1 style="font-size: 40px">&#x1F6AB;</h1>
Expand Down
3 changes: 2 additions & 1 deletion packages/editor-ui/src/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { IUser, ICredentialsResponse, IWorkflowDb } from '@/Interface';
import { EnterpriseEditionFeature, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
import { useSettingsStore } from '@/stores/settings.store';
import { hasPermission } from './rbac/permissions';
import { isUserGlobalOwner } from './utils/userUtils';

/**
* Old permissions implementation
Expand Down Expand Up @@ -43,7 +44,7 @@ export const parsePermissionsTable = (
table: IPermissionsTable,
): IPermissions => {
const genericTable: IPermissionsTable = [
{ name: UserRole.InstanceOwner, test: () => !!user?.isOwner },
{ name: UserRole.InstanceOwner, test: () => (user ? isUserGlobalOwner(user) : false) },
];

return [...genericTable, ...table].reduce(
Expand Down
4 changes: 2 additions & 2 deletions packages/editor-ui/src/rbac/checks/__tests__/hasRole.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useUsersStore } from '@/stores/users.store';
import { hasRole } from '@/rbac/checks';
import { ROLE } from '@/utils/userUtils';
import { ROLE } from '@/constants';

vi.mock('@/stores/users.store', () => ({
useUsersStore: vi.fn(),
Expand All @@ -12,7 +12,7 @@ describe('Checks', () => {
vi.mocked(useUsersStore).mockReturnValue({
currentUser: {
isDefaultUser: false,
role: 'global:owner',
role: ROLE.Owner,
},
} as ReturnType<typeof useUsersStore>);

Expand Down
2 changes: 1 addition & 1 deletion packages/editor-ui/src/rbac/checks/hasRole.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useUsersStore } from '@/stores/users.store';
import type { RBACPermissionCheck, RolePermissionOptions } from '@/types/rbac';
import { ROLE } from '@/utils/userUtils';
import { ROLE } from '@/constants';
import type { IRole } from '@/Interface';

export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles) => {
Expand Down
7 changes: 3 additions & 4 deletions packages/editor-ui/src/rbac/middleware/__tests__/role.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { roleMiddleware } from '@/rbac/middleware/role';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@/utils/userUtils';
import type { IUser } from '@/Interface';
import type { RouteLocationNormalized } from 'vue-router';
import { VIEWS } from '@/constants';
import { VIEWS, ROLE } from '@/constants';

vi.mock('@/stores/users.store', () => ({
useUsersStore: vi.fn(),
Expand All @@ -15,7 +14,7 @@ describe('Middleware', () => {
vi.mocked(useUsersStore).mockReturnValue({
currentUser: {
isDefaultUser: false,
role: 'global:owner',
role: ROLE.Owner,
} as IUser,
} as ReturnType<typeof useUsersStore>);

Expand Down Expand Up @@ -54,7 +53,7 @@ describe('Middleware', () => {
vi.mocked(useUsersStore).mockReturnValue({
currentUser: {
isDefaultUser: false,
role: 'global:owner',
role: ROLE.Owner,
} as IUser,
} as ReturnType<typeof useUsersStore>);

Expand Down
Loading

0 comments on commit d3f5cfb

Please sign in to comment.