Skip to content

Commit 0b09f20

Browse files
committed
feat: Enhance Balance Subscriptions webhook handling
- Add support for creating new webhooks with optional labels - Allow using existing webhooks by ID - Validate webhook event type and revocation status - Improve error handling for webhook creation and selection
1 parent f472a82 commit 0b09f20

File tree

1 file changed

+63
-17
lines changed
  • src/server/routes/balance-subscriptions

1 file changed

+63
-17
lines changed

src/server/routes/balance-subscriptions/add.ts

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { FastifyInstance } from "fastify";
33
import { StatusCodes } from "http-status-codes";
44
import { createBalanceSubscription } from "../../../shared/db/balance-subscriptions/create-balance-subscription";
55
import { insertWebhook } from "../../../shared/db/webhooks/create-webhook";
6+
import { getWebhook } from "../../../shared/db/webhooks/get-webhook";
67
import { balanceSubscriptionConfigSchema } from "../../../shared/schemas/balance-subscription-config";
78
import { WebhooksEventTypes } from "../../../shared/schemas/webhooks";
89
import { createCustomError } from "../../middleware/error";
@@ -13,19 +14,36 @@ import { getChainIdFromChain } from "../../utils/chain";
1314
import { isValidWebhookUrl } from "../../utils/validator";
1415
import { balanceSubscriptionSchema, toBalanceSubscriptionSchema } from "../../schemas/balance-subscription";
1516

16-
const requestBodySchema = Type.Object({
17-
chain: chainIdOrSlugSchema,
18-
contractAddress: Type.Optional(AddressSchema),
19-
walletAddress: AddressSchema,
20-
config: balanceSubscriptionConfigSchema,
21-
webhookUrl: Type.Optional(
17+
const webhookUrlSchema = Type.Object({
18+
webhookUrl: Type.String({
19+
description: "Webhook URL to create a new webhook",
20+
examples: ["https://example.com/webhook"],
21+
}),
22+
webhookLabel: Type.Optional(
2223
Type.String({
23-
description: "Webhook URL",
24-
examples: ["https://example.com/webhook"],
24+
description: "Optional label for the webhook when creating a new one",
25+
examples: ["My Balance Subscription Webhook"],
26+
minLength: 3,
2527
}),
2628
),
2729
});
2830

31+
const webhookIdSchema = Type.Object({
32+
webhookId: Type.Integer({
33+
description: "ID of an existing webhook to use",
34+
}),
35+
});
36+
37+
const requestBodySchema = Type.Intersect([
38+
Type.Object({
39+
chain: chainIdOrSlugSchema,
40+
contractAddress: Type.Optional(AddressSchema),
41+
walletAddress: AddressSchema,
42+
config: balanceSubscriptionConfigSchema,
43+
}),
44+
Type.Union([webhookUrlSchema, webhookIdSchema]),
45+
]);
46+
2947
const responseSchema = Type.Object({
3048
result: balanceSubscriptionSchema,
3149
});
@@ -49,13 +67,15 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {
4967
},
5068
},
5169
handler: async (request, reply) => {
52-
const { chain, contractAddress, walletAddress, config, webhookUrl } = request.body;
53-
70+
const { chain, contractAddress, walletAddress, config } = request.body;
5471
const chainId = await getChainIdFromChain(chain);
5572

56-
// Create the webhook (if provided).
57-
let webhookId: number | undefined;
58-
if (webhookUrl) {
73+
let finalWebhookId: number | undefined;
74+
75+
// Handle webhook creation or validation
76+
if ("webhookUrl" in request.body) {
77+
// Create new webhook
78+
const { webhookUrl, webhookLabel } = request.body;
5979
if (!isValidWebhookUrl(webhookUrl)) {
6080
throw createCustomError(
6181
"Invalid webhook URL. Make sure it starts with 'https://'.",
@@ -66,19 +86,45 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {
6686

6787
const webhook = await insertWebhook({
6888
eventType: WebhooksEventTypes.BALANCE_SUBSCRIPTION,
69-
name: "(Auto-generated)",
89+
name: webhookLabel || "(Auto-generated)",
7090
url: webhookUrl,
7191
});
72-
webhookId = webhook.id;
92+
finalWebhookId = webhook.id;
93+
} else {
94+
// Validate existing webhook
95+
const { webhookId } = request.body;
96+
const webhook = await getWebhook(webhookId);
97+
if (!webhook) {
98+
throw createCustomError(
99+
`Webhook with ID ${webhookId} not found.`,
100+
StatusCodes.NOT_FOUND,
101+
"NOT_FOUND",
102+
);
103+
}
104+
if (webhook.eventType !== WebhooksEventTypes.BALANCE_SUBSCRIPTION) {
105+
throw createCustomError(
106+
`Webhook with ID ${webhookId} has incorrect event type. Expected '${WebhooksEventTypes.BALANCE_SUBSCRIPTION}' but got '${webhook.eventType}'.`,
107+
StatusCodes.BAD_REQUEST,
108+
"BAD_REQUEST",
109+
);
110+
}
111+
if (webhook.revokedAt) {
112+
throw createCustomError(
113+
`Webhook with ID ${webhookId} has been revoked.`,
114+
StatusCodes.BAD_REQUEST,
115+
"BAD_REQUEST",
116+
);
117+
}
118+
finalWebhookId = webhookId;
73119
}
74120

75-
// Create the balance subscription.
121+
// Create the balance subscription
76122
const balanceSubscription = await createBalanceSubscription({
77123
chainId: chainId.toString(),
78124
contractAddress: contractAddress?.toLowerCase(),
79125
walletAddress: walletAddress.toLowerCase(),
80126
config,
81-
webhookId,
127+
webhookId: finalWebhookId,
82128
});
83129

84130
reply.status(StatusCodes.OK).send({

0 commit comments

Comments
 (0)