Skip to content

Health branch - #143

Open
OmniZlatoon wants to merge 3 commits into
SmartDropLabs:mainfrom
OmniZlatoon:health-branch
Open

Health branch#143
OmniZlatoon wants to merge 3 commits into
SmartDropLabs:mainfrom
OmniZlatoon:health-branch

Conversation

@OmniZlatoon

@OmniZlatoon OmniZlatoon commented Aug 21, 2026

Copy link
Copy Markdown

closes #123

### Walkthrough: Webhook Dispatch Issues & GET /health Status Aggregation

This walkthrough provides a comprehensive summary of the tasks completed to address the webhook dispatch documentation discrepancies and the health check status calculation gap.

Task 1: Resolving Webhook Dispatch Issues

Background Context
The codebase defined several pool.* events (such as pool.created, pool.assets_locked, etc.) as subscribable webhooks. However, the system's indexer only decodes airdrop lifecycle events, and there is no backend code path that dispatches any pool-related events. Furthermore, the test delivery endpoint (POST /webhooks/:id/test) sent a synthetic pool.assets_locked payload, which misled clients into believing pool events were fully functional.

Implementation Details
To resolve this without breaking existing database entries, third-party subscriptions, or tests, we chose to retain the POOL_EVENTS registry as a forward-looking placeholder while updating all relevant documentation and codebase comments to be explicit.

1. Codebase Registry Update
File modified:
webhookEvents.js
Change: Added an explicit caveat comment above POOL_EVENTS explaining that these events are not currently dispatched. This follows the exact pattern already established for AIRDROP_EVENTS.
Code Change:
javascript
// None of the pool events are actually dispatched by the codebase today (the
// event indexer only parses airdrop lifecycle events). They are registered
// here as forward-looking placeholders, but subscribing to them will not
// trigger any deliveries until the corresponding indexer and dispatch logic
// are implemented.
const POOL_EVENTS = Object.freeze([
'pool.created',
'pool.assets_locked',
'pool.assets_unlocked',
'pool.rewards_distributed',
'pool.closed',
]);

2. User Documentation Update
File modified:
README.md
Changes:
Updated the Webhook Delivery System setup section to group events by active (airdrop.failed) and planned (airdrop.created, airdrop.executing, airdrop.completed, recipient.claimed) status.
Updated the "Supported event types" table to explicitly label all pool.* events as Planned (not yet implemented).
Updated the worked registration POST /api/v1/webhooks example payload to use active events (airdrop.failed, price.alert) instead of unimplemented pool events.
Added a warning under #### Test endpoint to clarify that a successful test delivery (sending a synthetic pool.assets_locked payload) only validates the receiver's URL and does not imply that pool events are live in production.

3. OpenAPI Specification Update
File modified:
openapi.yaml
Changes:
Corrected the /api/v1/webhooks/{id}/test description and examples to indicate that it queues a synthetic pool.assets_locked payload instead of a ping event.
Added a warning box (⚠️ Warning) in the endpoint description clarifying the synthetic nature of the test delivery.
Expanded the WebhookEvent schema enum to contain the missing pool.* and price.alert events.
Classified the enum values in the WebhookEvent description into Active / implemented vs Planned / not yet implemented.
Updated all /api/v1/webhooks endpoint request and response examples to use active/implemented events (airdrop.failed / price.alert).

Task 2: GET /health Status Aggregation Integration
Background Context
The GET /health endpoint serves as the primary integration for external monitors, load balancers, or container probes. However, its top-level status field was only computed from Redis connectivity and two out of three background jobs (price_refresh and webhook_retry_worker). The health of the third background job (airdrop_expiry) was reported in the body but ignored in the aggregate status calculation. If airdrop_expiry stalled, the status remained 'ok'.

Implementation Details
We refactored the status computation to include all three jobs. Additionally, we extracted the logic into a named helper to make it loop-based, avoiding hardcoded boolean variables that are prone to future regression.

1. Core Refactoring
File modified:
index.js
Change: Implemented the computeAggregateStatus(redisConnected, jobHealths) helper function and updated the /health handler to invoke it.
Code Change:
javascript
/**

  • Computes the overall aggregate health status of the application based on Redis connection state
  • and a list of leader-elected background job health statistics.
  • Status levels:
    • unhealthy: Redis is disconnected, or any job is stalled.
    • degraded: No job is stalled, but at least one job is not yet healthy (meaning it's in its startup grace period).
    • ok: Redis is connected and all jobs are healthy.
      */
      function computeAggregateStatus(redisConnected, jobHealths) {
      if (!redisConnected) {
      return 'unhealthy';
      }
      const anyStalled = jobHealths.some((job) => job.stalled);
      if (anyStalled) {
      return 'unhealthy';
      }
      const anyUnhealthy = jobHealths.some((job) => !job.healthy);
      if (anyUnhealthy) {
      return 'degraded';
      }
      return 'ok';
      }
      // Inside app.get('/health', ...)
      const status = computeAggregateStatus(redisConnected, [
      priceRefreshHealth,
      webhookWorkerHealth,
      airdropExpiryHealth,
      ]);

2. Test Verification & Regression Coverage
File modified:
health.test.js
Changes:
Mocked ../src/jobs/airdropExpiry at the top level to return healthy stats by default. This avoids breaking existing status tests since the unstarted airdropExpiry job defaults to an unhealthy/pre-run state.
Updated the test that checks if the jobs response field contains price_refresh and webhook_retry_worker to also assert the presence and shape of airdrop_expiry.
Added a regression test asserting that if only the airdrop_expiry job is stalled (with others healthy), the top-level status degrades to 'unhealthy'.
Added a regression test asserting that if the airdrop_expiry job has not yet run (healthy: false, stalled: false), the status is 'degraded'.
Verification Summary
All automated tests in the repository were run to verify the correctness of the changes and guard against regressions:

bash
npm test
Test Suite Output
PASS test/webhookDispatcher.test.js
PASS test/airdrops-service.test.js
PASS test/auth.test.js
PASS test/eventPoller.test.js
PASS test/errorHandler.test.js
PASS test/prices.test.js
PASS test/webhooks.routes.test.js
PASS test/validate.test.js
PASS test/indexerParser.test.js
PASS test/paginationContract.test.js
PASS test/api-docs.test.js
PASS test/webhookSignature.test.js
PASS test/apiRateLimit.test.js
PASS test/config.test.js
PASS test/cors.test.js
PASS test/coingecko.test.js
PASS test/circuitBreaker.test.js
PASS test/deliveryRepository.test.js
PASS test/indexerRoutes.test.js
PASS test/alerts-routes.test.js
PASS test/stellarDex.test.js
PASS test/coinmarketcap.test.js
PASS test/airdrops.test.js
PASS test/priceWebSocket.test.js
PASS test/webhookRepository.test.js
PASS test/resilience.test.js
PASS test/priceOracle.test.js
PASS test/airdropExpiry.test.js
PASS test/rateLimit.test.js
PASS test/leaderElection.test.js
PASS test/priceOracleCircuit.test.js
PASS test/cacheWarm.test.js
PASS test/webhooks.test.js
PASS test/alerts.test.js
PASS test/webhookEvents.test.js
PASS test/indexerStore.test.js
PASS test/pagination.test.js
PASS test/requestId.test.js
PASS test/health.test.js
Test Suites: 39 passed, 39 total
Tests: 372 passed, 372 total
Snapshots: 0 total
Time: 7.005 s
Ran all test suites.
All 372 tests passed successfully.

@OmniZlatoon

Copy link
Copy Markdown
Author

Good morning sir @prodbycorne , please kindly review the PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant