Convert WebhookSubscription mutations (#8161)

* Convert 'WebhookSubscription' mutations

* add tests

* remove unnecessary assignment
This commit is contained in:
Hemachandar
2024-12-31 05:41:32 +05:30
committed by GitHub
parent 41832bbaf1
commit 5dd993adf5
3 changed files with 264 additions and 58 deletions
@@ -0,0 +1,239 @@
import {
buildAdmin,
buildUser,
buildWebhookSubscription,
} from "@server/test/factories";
import { getTestServer } from "@server/test/support";
const server = getTestServer();
describe("#webhookSubscriptions.list", () => {
it("should fail with status 401 unauthorized when user token is missing", async () => {
const res = await server.post("/api/webhookSubscriptions.list", {
body: {},
});
const body = await res.json();
expect(res.status).toEqual(401);
expect(body.message).toEqual("Authentication required");
});
it("should fail with status 403 forbidden for non-admin user", async () => {
const user = await buildUser();
const res = await server.post("/api/webhookSubscriptions.list", {
body: { token: user.getJwtToken() },
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body.message).toEqual("Admin role required");
});
it("should return the webhook subscriptions for the user's team", async () => {
const user = await buildAdmin();
const webhookSubscriptions = await Promise.all(
Array(20)
.fill(1)
.map(() =>
buildWebhookSubscription({
createdById: user.id,
teamId: user.teamId,
})
)
);
const res = await server.post("/api/webhookSubscriptions.list", {
body: { token: user.getJwtToken() },
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.data.length).toEqual(webhookSubscriptions.length);
});
});
describe("#webhookSubscriptions.create", () => {
it("should fail with status 401 unauthorized when user token is missing", async () => {
const res = await server.post("/api/webhookSubscriptions.create", {
body: {},
});
const body = await res.json();
expect(res.status).toEqual(401);
expect(body.message).toEqual("Authentication required");
});
it("should fail with status 403 forbidden for non-admin user", async () => {
const user = await buildUser();
const res = await server.post("/api/webhookSubscriptions.create", {
body: { token: user.getJwtToken() },
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body.message).toEqual("Admin role required");
});
it("should successfully create a webhook subscription", async () => {
const user = await buildAdmin();
const name = "Test webhook";
const url = "https://www.example.com";
const events = ["comments"];
const secret = "Test secret";
const res = await server.post("/api/webhookSubscriptions.create", {
body: {
token: user.getJwtToken(),
name,
url,
events,
secret,
},
});
const body = await res.json();
const webhook = body.data;
expect(res.status).toEqual(200);
expect(webhook.name).toEqual(name);
expect(webhook.url).toEqual(url);
expect(webhook.events).toEqual(events);
expect(webhook.secret).toEqual(secret);
expect(webhook.enabled).toEqual(true);
});
});
describe("#webhookSubscriptions.update", () => {
it("should fail with status 401 unauthorized when user token is missing", async () => {
const res = await server.post("/api/webhookSubscriptions.update", {
body: {},
});
const body = await res.json();
expect(res.status).toEqual(401);
expect(body.message).toEqual("Authentication required");
});
it("should fail with status 403 forbidden for non-admin user", async () => {
const user = await buildUser();
const res = await server.post("/api/webhookSubscriptions.update", {
body: { token: user.getJwtToken() },
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body.message).toEqual("Admin role required");
});
it("should successfully update a webhook subscription", async () => {
const user = await buildAdmin();
const name = "Updated webhook name";
const url = "https://www.example.com/update";
const events = ["comments"];
const existingWebhook = await buildWebhookSubscription({
name: "Created webhook name",
url: "https://www.example.com/create",
events: ["*"],
createdById: user.id,
teamId: user.teamId,
});
const res = await server.post("/api/webhookSubscriptions.update", {
body: {
token: user.getJwtToken(),
id: existingWebhook.id,
name,
url,
events,
},
});
const body = await res.json();
const webhook = body.data;
expect(res.status).toEqual(200);
expect(webhook.name).toEqual(name);
expect(webhook.url).toEqual(url);
expect(webhook.events).toEqual(events);
expect(webhook.enabled).toEqual(true);
});
it("should activate a disabled webhook subscription when it's updated", async () => {
const user = await buildAdmin();
const name = "Updated webhook name";
const url = "https://www.example.com/update";
const events = ["comments"];
const disabledWebhook = await buildWebhookSubscription({
name: "Created webhook name",
url: "https://www.example.com/create",
events: ["*"],
createdById: user.id,
teamId: user.teamId,
enabled: false,
});
const res = await server.post("/api/webhookSubscriptions.update", {
body: {
token: user.getJwtToken(),
id: disabledWebhook.id,
name,
url,
events,
},
});
const body = await res.json();
const webhook = body.data;
expect(res.status).toEqual(200);
expect(webhook.name).toEqual(name);
expect(webhook.url).toEqual(url);
expect(webhook.events).toEqual(events);
expect(webhook.enabled).toEqual(true);
});
});
describe("#webhookSubscriptions.delete", () => {
it("should fail with status 401 unauthorized when user token is missing", async () => {
const res = await server.post("/api/webhookSubscriptions.delete", {
body: {},
});
const body = await res.json();
expect(res.status).toEqual(401);
expect(body.message).toEqual("Authentication required");
});
it("should fail with status 403 forbidden for non-admin user", async () => {
const user = await buildUser();
const res = await server.post("/api/webhookSubscriptions.delete", {
body: { token: user.getJwtToken() },
});
const body = await res.json();
expect(res.status).toEqual(403);
expect(body.message).toEqual("Admin role required");
});
it("should successfully delete a webhook subscription", async () => {
const user = await buildAdmin();
const createdWebhook = await buildWebhookSubscription({
name: "Test webhook",
url: "https://www.example.com",
events: ["*"],
createdById: user.id,
teamId: user.teamId,
});
const res = await server.post("/api/webhookSubscriptions.delete", {
body: { token: user.getJwtToken(), id: createdWebhook.id },
});
const body = await res.json();
expect(res.status).toEqual(200);
expect(body.success).toEqual(true);
});
});
@@ -5,7 +5,7 @@ import { UserRole } from "@shared/types";
import auth from "@server/middlewares/authentication";
import { transaction } from "@server/middlewares/transaction";
import validate from "@server/middlewares/validate";
import { WebhookSubscription, Event } from "@server/models";
import { WebhookSubscription } from "@server/models";
import { authorize } from "@server/policies";
import pagination from "@server/routes/api/middlewares/pagination";
import { APIContext } from "@server/types";
@@ -20,7 +20,9 @@ router.post(
pagination(),
async (ctx: APIContext) => {
const { user } = ctx.state.auth;
authorize(user, "listWebhookSubscription", user.team);
const webhooks = await WebhookSubscription.findAll({
where: {
teamId: user.teamId,
@@ -43,34 +45,19 @@ router.post(
validate(T.WebhookSubscriptionsCreateSchema),
transaction(),
async (ctx: APIContext<T.WebhookSubscriptionsCreateReq>) => {
const { transaction } = ctx.state;
const { name, url, secret, events } = ctx.input.body;
const { user } = ctx.state.auth;
authorize(user, "createWebhookSubscription", user.team);
const { name, url, secret } = ctx.input.body;
const events: string[] = compact(ctx.input.body.events);
const webhookSubscription = await WebhookSubscription.create(
{
name,
events,
createdById: user.id,
teamId: user.teamId,
url,
enabled: true,
secret: isEmpty(secret) ? undefined : secret,
},
{ transaction }
);
await Event.createFromContext(ctx, {
name: "webhookSubscriptions.create",
modelId: webhookSubscription.id,
data: {
name,
url,
events,
},
const webhookSubscription = await WebhookSubscription.createWithCtx(ctx, {
name,
url,
events: compact(events),
enabled: true,
secret: isEmpty(secret) ? undefined : secret,
createdById: user.id,
teamId: user.teamId,
});
ctx.body = {
@@ -88,6 +75,7 @@ router.post(
const { id } = ctx.input.body;
const { user } = ctx.state.auth;
const { transaction } = ctx.state;
const webhookSubscription = await WebhookSubscription.findByPk(id, {
rejectOnEmpty: true,
lock: transaction.LOCK.UPDATE,
@@ -96,17 +84,7 @@ router.post(
authorize(user, "delete", webhookSubscription);
await webhookSubscription.destroy({ transaction });
await Event.createFromContext(ctx, {
name: "webhookSubscriptions.delete",
modelId: webhookSubscription.id,
data: {
name: webhookSubscription.name,
url: webhookSubscription.url,
events: webhookSubscription.events,
},
});
await webhookSubscription.destroyWithCtx(ctx);
ctx.body = {
success: true,
@@ -120,10 +98,10 @@ router.post(
validate(T.WebhookSubscriptionsUpdateSchema),
transaction(),
async (ctx: APIContext<T.WebhookSubscriptionsUpdateReq>) => {
const { id, name, url, secret } = ctx.input.body;
const { id, name, url, secret, events } = ctx.input.body;
const { user } = ctx.state.auth;
const { transaction } = ctx.state;
const events: string[] = compact(ctx.input.body.events);
const webhookSubscription = await WebhookSubscription.findByPk(id, {
rejectOnEmpty: true,
lock: transaction.LOCK.UPDATE,
@@ -132,25 +110,12 @@ router.post(
authorize(user, "update", webhookSubscription);
await webhookSubscription.update(
{
name,
url,
events,
enabled: true,
secret: isEmpty(secret) ? undefined : secret,
},
{ transaction }
);
await Event.createFromContext(ctx, {
name: "webhookSubscriptions.update",
modelId: webhookSubscription.id,
data: {
name: webhookSubscription.name,
url: webhookSubscription.url,
events: webhookSubscription.events,
},
await webhookSubscription.updateWithCtx(ctx, {
name,
url,
events: compact(events),
enabled: true,
secret: isEmpty(secret) ? undefined : secret,
});
ctx.body = {
+2
View File
@@ -44,6 +44,8 @@ class WebhookSubscription extends ParanoidModel<
InferAttributes<WebhookSubscription>,
Partial<InferCreationAttributes<WebhookSubscription>>
> {
static eventNamespace = "webhookSubscriptions";
@NotEmpty
@Length({ max: 255, msg: "Webhook name be less than 255 characters" })
@Column