mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 04:19:32 +00:00
feat(organization): listUserInvitations adds the ability to list all invitations for a given user (#3121)
* feat(org): listUserInvitations This PR introduces in the org plugin. * chore: lint
This commit is contained in:
@@ -560,7 +560,7 @@ await authClient.organization.getInvitation({
|
||||
|
||||
### List Invitations
|
||||
|
||||
To list all invitations you can use the `listInvitations` function provided by the client.
|
||||
To list all invitations for a given organization you can use the `listInvitations` function provided by the client.
|
||||
|
||||
```ts title="auth-client.ts"
|
||||
const invitations = await authClient.organization.listInvitations({
|
||||
@@ -570,6 +570,30 @@ const invitations = await authClient.organization.listInvitations({
|
||||
})
|
||||
```
|
||||
|
||||
### List user invitations
|
||||
|
||||
To list all invitations for a given user you can use the `listUserInvitations` function provided by the client.
|
||||
|
||||
```ts title="auth-client.ts"
|
||||
const invitations = await authClient.organization.listUserInvitations()
|
||||
```
|
||||
|
||||
On the server, you can pass the user ID as a query parameter.
|
||||
|
||||
```ts title="api.ts"
|
||||
const invitations = await auth.api.listUserInvitations({
|
||||
query: {
|
||||
email: "user@example.com"
|
||||
}
|
||||
})
|
||||
```
|
||||
<Callout type="warn">
|
||||
The `email` query parameter is only available on the server to query for invitations for a specific user.
|
||||
</Callout>
|
||||
|
||||
|
||||
|
||||
|
||||
## Members
|
||||
|
||||
### Remove Member
|
||||
|
||||
@@ -561,7 +561,13 @@ export const getOrgAdapter = (
|
||||
});
|
||||
return invitations;
|
||||
},
|
||||
|
||||
listUserInvitations: async (email: string) => {
|
||||
const invitations = await adapter.findMany<Invitation>({
|
||||
model: "invitation",
|
||||
where: [{ field: "email", value: email }],
|
||||
});
|
||||
return invitations;
|
||||
},
|
||||
createInvitation: async ({
|
||||
invitation,
|
||||
user,
|
||||
|
||||
@@ -703,10 +703,11 @@ describe("organization", async (it) => {
|
||||
password: userOverLimit.password,
|
||||
name: userOverLimit.name,
|
||||
});
|
||||
const { res, headers: headers2 } = await signInWithUser(
|
||||
const { headers: headers2 } = await signInWithUser(
|
||||
userOverLimit2.email,
|
||||
userOverLimit2.password,
|
||||
);
|
||||
|
||||
await client.signUp.email(
|
||||
{
|
||||
email: userOverLimit2.email,
|
||||
@@ -738,6 +739,92 @@ describe("organization", async (it) => {
|
||||
});
|
||||
expect(getFullOrganization.data?.members.length).toBe(6);
|
||||
});
|
||||
|
||||
it("should allow listing invitations for an org", async () => {
|
||||
const invitations = await client.organization.listInvitations({
|
||||
query: {
|
||||
organizationId: organizationId,
|
||||
},
|
||||
fetchOptions: {
|
||||
headers: headers,
|
||||
},
|
||||
});
|
||||
expect(invitations.data?.length).toBe(4);
|
||||
});
|
||||
|
||||
it("should allow listing invitations for a user using authClient", async () => {
|
||||
const rng = crypto.randomUUID();
|
||||
const user = {
|
||||
email: `${rng}@email.com`,
|
||||
password: rng,
|
||||
name: rng,
|
||||
};
|
||||
const rng2 = crypto.randomUUID();
|
||||
const orgAdminUser = {
|
||||
email: `${rng2}@email.com`,
|
||||
password: rng2,
|
||||
name: rng2,
|
||||
};
|
||||
await auth.api.signUpEmail({
|
||||
body: user,
|
||||
});
|
||||
await auth.api.signUpEmail({
|
||||
body: orgAdminUser,
|
||||
});
|
||||
const { headers: headers2, res: session } = await signInWithUser(
|
||||
user.email,
|
||||
user.password,
|
||||
);
|
||||
const { headers: adminHeaders, res: adminSession } = await signInWithUser(
|
||||
orgAdminUser.email,
|
||||
orgAdminUser.password,
|
||||
);
|
||||
const orgRng = crypto.randomUUID();
|
||||
const org = await auth.api.createOrganization({
|
||||
body: {
|
||||
name: orgRng,
|
||||
slug: orgRng,
|
||||
},
|
||||
headers: adminHeaders,
|
||||
});
|
||||
const invitation = await client.organization.inviteMember({
|
||||
organizationId: org?.id,
|
||||
email: user.email,
|
||||
role: "member",
|
||||
fetchOptions: {
|
||||
headers: adminHeaders,
|
||||
},
|
||||
});
|
||||
const userInvitations = await client.organization.listUserInvitations({
|
||||
fetchOptions: {
|
||||
headers: headers2,
|
||||
},
|
||||
});
|
||||
expect(userInvitations.data?.[0].id).toBe(invitation.data?.id);
|
||||
expect(userInvitations.data?.length).toBe(1);
|
||||
});
|
||||
|
||||
it("should allow listing invitations for a user using server", async () => {
|
||||
const orgInvitations = await client.organization.listInvitations({
|
||||
fetchOptions: {
|
||||
headers,
|
||||
},
|
||||
});
|
||||
|
||||
if (!orgInvitations.data?.[0].email) throw new Error("No email found");
|
||||
|
||||
const invitations = await auth.api.listUserInvitations({
|
||||
query: {
|
||||
email: orgInvitations.data?.[0].email,
|
||||
},
|
||||
});
|
||||
|
||||
expect(invitations?.length).toBe(
|
||||
orgInvitations.data.filter(
|
||||
(x) => x.email === orgInvitations.data?.[0].email,
|
||||
).length,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("access control", async (it) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
getInvitation,
|
||||
listInvitations,
|
||||
rejectInvitation,
|
||||
listUserInvitations,
|
||||
} from "./routes/crud-invites";
|
||||
import {
|
||||
addMember,
|
||||
@@ -386,6 +387,7 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
||||
acceptInvitation,
|
||||
getInvitation,
|
||||
rejectInvitation,
|
||||
listUserInvitations,
|
||||
checkOrganizationSlug,
|
||||
addMember: addMember<O>(),
|
||||
removeMember,
|
||||
|
||||
@@ -745,3 +745,44 @@ export const listInvitations = createAuthEndpoint(
|
||||
return ctx.json(invitations);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* List all invitations recieved for a user
|
||||
*/
|
||||
export const listUserInvitations = createAuthEndpoint(
|
||||
"/organization/list-user-invitations",
|
||||
{
|
||||
method: "GET",
|
||||
use: [orgMiddleware],
|
||||
query: z
|
||||
.object({
|
||||
email: z
|
||||
.string({
|
||||
description:
|
||||
"The email of the user to list invitations for. This only works for server side API calls.",
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
},
|
||||
async (ctx) => {
|
||||
const session = await getSessionFromCtx(ctx);
|
||||
|
||||
if (ctx.request && ctx.query?.email) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "User email cannot be passed for client side API calls.",
|
||||
});
|
||||
}
|
||||
|
||||
const userEmail = session?.user.email || ctx.query?.email;
|
||||
if (!userEmail) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "Missing session headers, or email query parameter.",
|
||||
});
|
||||
}
|
||||
const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
|
||||
|
||||
const invitations = await adapter.listUserInvitations(userEmail);
|
||||
return ctx.json(invitations);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user