Skip to content

Commit

Permalink
fix: Fix user reinvites on FE and BE (#8261)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivov authored Jan 9, 2024
1 parent b1c1372 commit 0dabe5c
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 13 deletions.
2 changes: 1 addition & 1 deletion packages/cli/src/services/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export class UserService {
const usersInvited = await this.sendEmails(
owner,
Object.fromEntries(createdUsers),
toCreateUsers[0].role, // same role for all invited users
attributes[0].role, // same role for all invited users
);

return { usersInvited, usersCreated: toCreateUsers.map(({ email }) => email) };
Expand Down
23 changes: 23 additions & 0 deletions packages/cli/test/integration/invitations.api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,29 @@ describe('POST /invitations', () => {
assertInvitedUsersOnDb(storedUser);
});

test('should reinvite member', async () => {
mailer.invite.mockResolvedValue({ emailSent: false });

await ownerAgent.post('/invitations').send([{ email: randomEmail(), role: 'member' }]);

await ownerAgent
.post('/invitations')
.send([{ email: randomEmail(), role: 'member' }])
.expect(200);
});

test('should reinvite admin if licensed', async () => {
license.isAdvancedPermissionsLicensed.mockReturnValue(true);
mailer.invite.mockResolvedValue({ emailSent: false });

await ownerAgent.post('/invitations').send([{ email: randomEmail(), role: 'admin' }]);

await ownerAgent
.post('/invitations')
.send([{ email: randomEmail(), role: 'admin' }])
.expect(200);
});

test('should fail to create admin shell if not licensed', async () => {
license.isAdvancedPermissionsLicensed.mockReturnValue(false);
mailer.invite.mockResolvedValue({ emailSent: false });
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/Interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,8 @@ export type IPersonalizationSurveyVersions =

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

export type InvitableRoleName = 'member' | 'admin';

export interface IUserResponse {
id: string;
firstName?: string;
Expand Down
9 changes: 7 additions & 2 deletions packages/editor-ui/src/api/invitation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { CurrentUserResponse, IInviteResponse, IRestApiContext, IRole } from '@/Interface';
import type {
CurrentUserResponse,
IInviteResponse,
IRestApiContext,
InvitableRoleName,
} from '@/Interface';
import type { IDataObject } from 'n8n-workflow';
import { makeRestApiRequest } from '@/utils/apiUtils';

Expand All @@ -12,7 +17,7 @@ type AcceptInvitationParams = {

export async function inviteUsers(
context: IRestApiContext,
params: Array<{ email: string; role: IRole }>,
params: Array<{ email: string; role: InvitableRoleName }>,
) {
return makeRestApiRequest<IInviteResponse[]>(context, 'POST', '/invitations', params);
}
Expand Down
11 changes: 6 additions & 5 deletions packages/editor-ui/src/stores/users.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
IUserResponse,
IUsersState,
CurrentUserResponse,
InvitableRoleName,
} from '@/Interface';
import { getCredentialPermissions } from '@/permissions';
import { getPersonalizedNodeTypes, ROLE } from '@/utils/userUtils';
Expand Down Expand Up @@ -302,7 +303,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
const users = await getUsers(rootStore.getRestApiContext);
this.addUsers(users);
},
async inviteUsers(params: Array<{ email: string; role: IRole }>): Promise<IInviteResponse[]> {
async inviteUsers(
params: Array<{ email: string; role: InvitableRoleName }>,
): Promise<IInviteResponse[]> {
const rootStore = useRootStore();
const users = await inviteUsers(rootStore.getRestApiContext, params);
this.addUsers(
Expand All @@ -314,11 +317,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
);
return users;
},
async reinviteUser(params: { email: string }): Promise<void> {
async reinviteUser({ email, role }: { email: string; role: InvitableRoleName }): Promise<void> {
const rootStore = useRootStore();
const invitationResponse = await inviteUsers(rootStore.getRestApiContext, [
{ email: params.email },
]);
const invitationResponse = await inviteUsers(rootStore.getRestApiContext, [{ email, role }]);
if (!invitationResponse[0].user.emailSent) {
throw Error(invitationResponse[0].error);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/editor-ui/src/utils/apiUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export async function request(config: {
baseURL: string;
endpoint: string;
headers?: IDataObject;
data?: IDataObject;
data?: IDataObject | IDataObject[];
withCredentials?: boolean;
}) {
const { method, baseURL, endpoint, headers, data } = config;
Expand Down Expand Up @@ -119,7 +119,7 @@ export async function makeRestApiRequest<T>(
context: IRestApiContext,
method: Method,
endpoint: string,
data?: IDataObject,
data?: IDataObject | IDataObject[],
) {
const response = await request({
method,
Expand Down
12 changes: 9 additions & 3 deletions packages/editor-ui/src/views/SettingsUsersView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, VIEWS } from '@/constants';
import type { IUser, IUserListAction } from '@/Interface';
import type { IUser, IUserListAction, InvitableRoleName } from '@/Interface';
import { useToast } from '@/composables/useToast';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
Expand Down Expand Up @@ -207,9 +207,15 @@ export default defineComponent({
},
async onReinvite(userId: string) {
const user = this.usersStore.getUserById(userId);
if (user?.email) {
if (user?.email && user?.globalRole) {
if (!['admin', 'member'].includes(user.globalRole.name)) {
throw new Error('Invalid role name on reinvite');
}
try {
await this.usersStore.reinviteUser({ email: user.email });
await this.usersStore.reinviteUser({
email: user.email,
role: user.globalRole.name as InvitableRoleName,
});
this.showToast({
type: 'success',
title: this.$locale.baseText('settings.users.inviteResent'),
Expand Down

0 comments on commit 0dabe5c

Please sign in to comment.