refactor: Extract Invitation routes to InvitationController (no-changelog) (#7726)

This PR:

- Creates `InvitationController`
- Moves `POST /users` to `POST /invitations` and move related test to
`invitations.api.tests`
- Moves `POST /users/:id` to `POST /invitations/:id/accept` and move
related test to `invitations.api.tests`
- Adjusts FE to use new endpoints
- Moves all the invitation logic to the `UserService`

---------

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
Ricardo Espinoza
2023-11-16 12:39:43 -05:00
committed by GitHub
parent e2ffd397fc
commit 8e0ae3cf8c
17 changed files with 713 additions and 624 deletions

View File

@@ -0,0 +1,25 @@
import type { CurrentUserResponse, IInviteResponse, IRestApiContext } from '@/Interface';
import type { IDataObject } from 'n8n-workflow';
import { makeRestApiRequest } from '@/utils/apiUtils';
type AcceptInvitationParams = {
inviterId: string;
inviteeId: string;
firstName: string;
lastName: string;
password: string;
};
export async function inviteUsers(context: IRestApiContext, params: Array<{ email: string }>) {
return makeRestApiRequest<IInviteResponse[]>(context, 'POST', '/invitations', params);
}
export async function acceptInvitation(context: IRestApiContext, params: AcceptInvitationParams) {
const { inviteeId, ...props } = params;
return makeRestApiRequest<CurrentUserResponse>(
context,
'POST',
`/invitations/${params.inviteeId}/accept`,
props as unknown as IDataObject,
);
}

View File

@@ -1,6 +1,5 @@
import type {
CurrentUserResponse,
IInviteResponse,
IPersonalizationLatestVersion,
IRestApiContext,
IUserResponse,
@@ -124,17 +123,6 @@ export async function getUsers(context: IRestApiContext): Promise<IUserResponse[
return makeRestApiRequest(context, 'GET', '/users');
}
export async function inviteUsers(
context: IRestApiContext,
params: Array<{ email: string }>,
): Promise<IInviteResponse[]> {
return makeRestApiRequest(context, 'POST', '/users', params as unknown as IDataObject);
}
export async function reinvite(context: IRestApiContext, { id }: { id: string }): Promise<void> {
await makeRestApiRequest(context, 'POST', `/users/${id}/reinvite`);
}
export async function getInviteLink(
context: IRestApiContext,
{ id }: { id: string },

View File

@@ -1,16 +1,13 @@
import {
changePassword,
deleteUser,
getInviteLink,
getPasswordResetLink,
getUsers,
inviteUsers,
login,
loginCurrentUser,
logout,
sendForgotPasswordEmail,
setupOwner,
signup,
submitPersonalizationSurvey,
updateCurrentUser,
updateCurrentUserPassword,
@@ -40,6 +37,7 @@ import { useUIStore } from './ui.store';
import { useCloudPlanStore } from './cloudPlan.store';
import { disableMfa, enableMfa, getMfaQR, verifyMfaToken } from '@/api/mfa';
import { confirmEmail, getCloudUserInfo } from '@/api/cloudPlans';
import { inviteUsers, acceptInvitation } from '@/api/invitation';
const isDefaultUser = (user: IUserResponse | null) =>
Boolean(user && user.isPending && user.globalRole && user.globalRole.name === ROLE.Owner);
@@ -233,7 +231,7 @@ export const useUsersStore = defineStore(STORES.USERS, {
const rootStore = useRootStore();
return validateSignupToken(rootStore.getRestApiContext, params);
},
async signup(params: {
async acceptInvitation(params: {
inviteeId: string;
inviterId: string;
firstName: string;
@@ -241,7 +239,7 @@ export const useUsersStore = defineStore(STORES.USERS, {
password: string;
}): Promise<void> {
const rootStore = useRootStore();
const user = await signup(rootStore.getRestApiContext, params);
const user = await acceptInvitation(rootStore.getRestApiContext, params);
if (user) {
this.addUsers([user]);
this.currentUserId = user.id;
@@ -336,10 +334,6 @@ export const useUsersStore = defineStore(STORES.USERS, {
throw Error(invitationResponse[0].error);
}
},
async getUserInviteLink(params: { id: string }): Promise<{ link: string }> {
const rootStore = useRootStore();
return getInviteLink(rootStore.getRestApiContext, params);
},
async getUserPasswordResetLink(params: { id: string }): Promise<{ link: string }> {
const rootStore = useRootStore();
return getPasswordResetLink(rootStore.getRestApiContext, params);

View File

@@ -98,11 +98,11 @@ async function request(config: {
}
}
export async function makeRestApiRequest(
export async function makeRestApiRequest<T>(
context: IRestApiContext,
method: Method,
endpoint: string,
data?: IDataObject,
data?: any,
) {
const response = await request({
method,
@@ -113,7 +113,7 @@ export async function makeRestApiRequest(
});
// @ts-ignore all cli rest api endpoints return data wrapped in `data` key
return response.data;
return response.data as T;
}
export async function get(

View File

@@ -83,14 +83,8 @@ export default defineComponent({
};
},
async mounted() {
const inviterId =
!this.$route.query.inviterId || typeof this.$route.query.inviterId !== 'string'
? null
: this.$route.query.inviterId;
const inviteeId =
!this.$route.query.inviteeId || typeof this.$route.query.inviteeId !== 'string'
? null
: this.$route.query.inviteeId;
const inviterId = this.getQueryParameter('inviterId');
const inviteeId = this.getQueryParameter('inviteeId');
try {
if (!inviterId || !inviteeId) {
throw new Error(this.$locale.baseText('auth.signup.missingTokenError'));
@@ -129,7 +123,7 @@ export default defineComponent({
try {
this.loading = true;
await this.usersStore.signup({
await this.usersStore.acceptInvitation({
...values,
inviterId: this.inviterId,
inviteeId: this.inviteeId,
@@ -153,6 +147,11 @@ export default defineComponent({
}
this.loading = false;
},
getQueryParameter(key: 'inviterId' | 'inviteeId'): string | null {
return !this.$route.query[key] || typeof this.$route.query[key] !== 'string'
? null
: (this.$route.query[key] as string);
},
},
});
</script>