Team membership webhook (#234)

* fixed webhook docs type

* fixed docs

* added team memberships webhook

* changed docs sidebar width

* updated tests
This commit is contained in:
Zai Shi 2024-09-08 23:52:10 +02:00 committed by GitHub
parent af53d0ca98
commit bb7074a29e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 65 additions and 10 deletions

View File

@ -10,6 +10,7 @@ import { KnownErrors } from "@stackframe/stack-shared";
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
import { PrismaTransaction } from "@/lib/types";
import { createLazyProxy } from "@stackframe/stack-shared/dist/utils/proxies";
import { sendTeamMembershipCreatedWebhook, sendTeamMembershipDeletedWebhook } from "@/lib/webhooks";
export async function addUserToTeam(tx: PrismaTransaction, options: {
@ -91,12 +92,22 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
});
});
return {};
const data = {
team_id: params.team_id,
user_id: userId,
};
await sendTeamMembershipCreatedWebhook({
projectId: auth.project.id,
data,
});
return data;
},
onDelete: async ({ auth, params }) => {
await prismaClient.$transaction(async (tx) => {
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);
const userId = getIdFromUserIdOrMe(params.user_id, auth.user);
await prismaClient.$transaction(async (tx) => {
// Users are always allowed to remove themselves from a team
// Only users with the $remove_members permission can remove other users
if (auth.type === 'client') {
@ -129,5 +140,13 @@ export const teamMembershipsCrudHandlers = createLazyProxy(() => createCrudHandl
},
});
});
await sendTeamMembershipDeletedWebhook({
projectId: auth.project.id,
data: {
team_id: params.team_id,
user_id: userId,
},
});
},
}));

View File

@ -541,7 +541,6 @@ export const usersCrudHandlers = createLazyProxy(() => createCrudHandlers(usersC
});
}
await sendUserCreatedWebhook({
projectId: auth.project.id,
data: result,

View File

@ -57,7 +57,10 @@ export function parseWebhookOpenAPI(options: {
metadata: webhook.metadata,
method: 'POST',
path: webhook.type,
requestBodyDesc: undefinedIfMixed(webhook.schema.describe()) || yupObject().describe(),
requestBodyDesc: undefinedIfMixed(yupObject({
type: yupString().required().meta({ openapiField: { description: webhook.type, exampleValue: webhook.type }}),
data: webhook.schema.required(),
}).describe()) || yupObject().describe(),
responseTypeDesc: yupString().oneOf(['json']).describe(),
statusCodeDesc: yupNumber().oneOf([200]).describe(),
}),

View File

@ -1,3 +1,4 @@
import { teamMembershipCreatedWebhookEvent, teamMembershipDeletedWebhookEvent } from "@stackframe/stack-shared/dist/interface/crud/team-memberships";
import { teamCreatedWebhookEvent, teamDeletedWebhookEvent, teamUpdatedWebhookEvent } from "@stackframe/stack-shared/dist/interface/crud/teams";
import { userCreatedWebhookEvent, userDeletedWebhookEvent, userUpdatedWebhookEvent } from "@stackframe/stack-shared/dist/interface/crud/users";
import { WebhookEvent } from "@stackframe/stack-shared/dist/interface/webhooks";
@ -51,3 +52,5 @@ export const sendUserDeletedWebhook = createWebhookSender(userDeletedWebhookEven
export const sendTeamCreatedWebhook = createWebhookSender(teamCreatedWebhookEvent);
export const sendTeamUpdatedWebhook = createWebhookSender(teamUpdatedWebhookEvent);
export const sendTeamDeletedWebhook = createWebhookSender(teamDeletedWebhookEvent);
export const sendTeamMembershipCreatedWebhook = createWebhookSender(teamMembershipCreatedWebhookEvent);
export const sendTeamMembershipDeletedWebhook = createWebhookSender(teamMembershipDeletedWebhookEvent);

View File

@ -49,7 +49,10 @@ it("creates a team and manage users on the server", async ({ expect }) => {
expect(response).toMatchInlineSnapshot(`
NiceResponse {
"status": 201,
"body": {},
"body": {
"team_id": "<stripped UUID>",
"user_id": "<stripped UUID>",
},
"headers": Headers { <some fields may have been hidden> },
}
`);

View File

@ -176,6 +176,7 @@ layout:
content-width: 40rem
disable-header: false
header-height: 60px
sidebar-width: 300px
favicon: ./docs/assets/favicon.ico
logo:
light: ./docs/assets/logo-light-mode.svg

View File

@ -11,7 +11,7 @@ In the Stack dashboard, you can create a webhook endpoint in the "Webhooks" sect
```json
{
"event": "team.created",
"type": "team.created",
"data": {
"id": "2209422a-eef7-4668-967d-be79409972c5",
"display_name": "My Team",

View File

@ -1,7 +1,11 @@
import { CrudTypeOf, createCrud } from "../../crud";
import { yupMixed, yupObject } from "../../schema-fields";
import { yupMixed, yupObject, yupString } from "../../schema-fields";
import { WebhookEvent } from "../webhooks";
export const teamMembershipsCrudClientReadSchema = yupObject({}).required();
export const teamMembershipsCrudClientReadSchema = yupObject({
team_id: yupString().required(),
user_id: yupString().required(),
}).required();
export const teamMembershipsCrudServerCreateSchema = yupObject({}).required();
export const teamMembershipsCrudClientDeleteSchema = yupMixed();
@ -29,4 +33,24 @@ export const teamMembershipsCrud = createCrud({
},
},
});
export type TeamMembershipsCrud = CrudTypeOf<typeof teamMembershipsCrud>;
export type TeamMembershipsCrud = CrudTypeOf<typeof teamMembershipsCrud>;
export const teamMembershipCreatedWebhookEvent = {
type: "team_membership.created",
schema: teamMembershipsCrud.server.readSchema,
metadata: {
summary: "Team Membership Created",
description: "This event is triggered when a user is added to a team.",
tags: ["Teams"],
},
} satisfies WebhookEvent<typeof teamMembershipsCrud.server.readSchema>;
export const teamMembershipDeletedWebhookEvent = {
type: "team_membership.deleted",
schema: teamMembershipsCrud.server.readSchema,
metadata: {
summary: "Team Membership Deleted",
description: "This event is triggered when a user is removed from a team.",
tags: ["Teams"],
},
} satisfies WebhookEvent<typeof teamMembershipsCrud.server.readSchema>;

View File

@ -1,4 +1,5 @@
import * as yup from "yup";
import { teamMembershipCreatedWebhookEvent, teamMembershipDeletedWebhookEvent } from "./crud/team-memberships";
import { teamCreatedWebhookEvent, teamDeletedWebhookEvent, teamUpdatedWebhookEvent } from "./crud/teams";
import { userCreatedWebhookEvent, userDeletedWebhookEvent, userUpdatedWebhookEvent } from "./crud/users";
@ -19,4 +20,6 @@ export const webhookEvents = [
teamCreatedWebhookEvent,
teamUpdatedWebhookEvent,
teamDeletedWebhookEvent,
teamMembershipCreatedWebhookEvent,
teamMembershipDeletedWebhookEvent,
] as const;