Skip to content

Commit

Permalink
feat: Enhance Balance Subscriptions webhook handling
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
d4mr committed Feb 10, 2025
1 parent f472a82 commit 0b09f20
Showing 1 changed file with 63 additions and 17 deletions.
80 changes: 63 additions & 17 deletions src/server/routes/balance-subscriptions/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { createBalanceSubscription } from "../../../shared/db/balance-subscriptions/create-balance-subscription";
import { insertWebhook } from "../../../shared/db/webhooks/create-webhook";
import { getWebhook } from "../../../shared/db/webhooks/get-webhook";
import { balanceSubscriptionConfigSchema } from "../../../shared/schemas/balance-subscription-config";
import { WebhooksEventTypes } from "../../../shared/schemas/webhooks";
import { createCustomError } from "../../middleware/error";
Expand All @@ -13,19 +14,36 @@ import { getChainIdFromChain } from "../../utils/chain";
import { isValidWebhookUrl } from "../../utils/validator";
import { balanceSubscriptionSchema, toBalanceSubscriptionSchema } from "../../schemas/balance-subscription";

const requestBodySchema = Type.Object({
chain: chainIdOrSlugSchema,
contractAddress: Type.Optional(AddressSchema),
walletAddress: AddressSchema,
config: balanceSubscriptionConfigSchema,
webhookUrl: Type.Optional(
const webhookUrlSchema = Type.Object({
webhookUrl: Type.String({
description: "Webhook URL to create a new webhook",
examples: ["https://example.com/webhook"],
}),
webhookLabel: Type.Optional(
Type.String({
description: "Webhook URL",
examples: ["https://example.com/webhook"],
description: "Optional label for the webhook when creating a new one",
examples: ["My Balance Subscription Webhook"],
minLength: 3,
}),
),
});

const webhookIdSchema = Type.Object({
webhookId: Type.Integer({
description: "ID of an existing webhook to use",
}),
});

const requestBodySchema = Type.Intersect([
Type.Object({
chain: chainIdOrSlugSchema,
contractAddress: Type.Optional(AddressSchema),
walletAddress: AddressSchema,
config: balanceSubscriptionConfigSchema,
}),
Type.Union([webhookUrlSchema, webhookIdSchema]),
]);

const responseSchema = Type.Object({
result: balanceSubscriptionSchema,
});
Expand All @@ -49,13 +67,15 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {
},
},
handler: async (request, reply) => {
const { chain, contractAddress, walletAddress, config, webhookUrl } = request.body;

const { chain, contractAddress, walletAddress, config } = request.body;
const chainId = await getChainIdFromChain(chain);

// Create the webhook (if provided).
let webhookId: number | undefined;
if (webhookUrl) {
let finalWebhookId: number | undefined;

// Handle webhook creation or validation
if ("webhookUrl" in request.body) {
// Create new webhook
const { webhookUrl, webhookLabel } = request.body;
if (!isValidWebhookUrl(webhookUrl)) {
throw createCustomError(
"Invalid webhook URL. Make sure it starts with 'https://'.",
Expand All @@ -66,19 +86,45 @@ export async function addBalanceSubscriptionRoute(fastify: FastifyInstance) {

const webhook = await insertWebhook({
eventType: WebhooksEventTypes.BALANCE_SUBSCRIPTION,
name: "(Auto-generated)",
name: webhookLabel || "(Auto-generated)",
url: webhookUrl,
});
webhookId = webhook.id;
finalWebhookId = webhook.id;
} else {
// Validate existing webhook
const { webhookId } = request.body;
const webhook = await getWebhook(webhookId);
if (!webhook) {
throw createCustomError(
`Webhook with ID ${webhookId} not found.`,
StatusCodes.NOT_FOUND,
"NOT_FOUND",
);
}
if (webhook.eventType !== WebhooksEventTypes.BALANCE_SUBSCRIPTION) {
throw createCustomError(
`Webhook with ID ${webhookId} has incorrect event type. Expected '${WebhooksEventTypes.BALANCE_SUBSCRIPTION}' but got '${webhook.eventType}'.`,
StatusCodes.BAD_REQUEST,
"BAD_REQUEST",
);
}
if (webhook.revokedAt) {
throw createCustomError(
`Webhook with ID ${webhookId} has been revoked.`,
StatusCodes.BAD_REQUEST,
"BAD_REQUEST",
);
}
finalWebhookId = webhookId;
}

// Create the balance subscription.
// Create the balance subscription
const balanceSubscription = await createBalanceSubscription({
chainId: chainId.toString(),
contractAddress: contractAddress?.toLowerCase(),
walletAddress: walletAddress.toLowerCase(),
config,
webhookId,
webhookId: finalWebhookId,
});

reply.status(StatusCodes.OK).send({
Expand Down

0 comments on commit 0b09f20

Please sign in to comment.