Skip to content

feat: add Maple alerts integration#317

Merged
Makisuo merged 1 commit intomainfrom
feat/maple-alerts-integration
Apr 26, 2026
Merged

feat: add Maple alerts integration#317
Makisuo merged 1 commit intomainfrom
feat/maple-alerts-integration

Conversation

@Makisuo
Copy link
Copy Markdown
Collaborator

@Makisuo Makisuo commented Apr 26, 2026

Summary

  • Adds Maple (maple.dev, an OpenTelemetry observability platform) as a new webhook-based integration that posts alert triggers, resolves, renotifies, and tests into a Hazel channel as rich embeds.
  • Mirrors the existing Railway / OpenStatus pattern — no new infrastructure; reuses the channel webhook + integration bot machinery.
  • Accepts Maple's standard buildPayload() JSON unchanged on a new /maple route, so there's no parallel payload schema between the two systems.

Changes

Integrations package (@hazel/integrations)

  • common/bot-configs.ts — register maple in WebhookProvider with the maple.dev Brandfetch icon.
  • New src/maple/ module: payloads.ts (TS interfaces matching Maple's webhook payload), colors.ts (severity → color/badge mapping for trigger / resolve / renotify / test), embed-builder.ts (buildMapleEmbed), plus index.ts / browser.ts exports.
  • package.json — wire ./maple and ./maple/browser exports.

Domain

  • http/incoming-webhooks.ts — new MaplePayload Effect Schema and an executeMaple endpoint at POST /webhooks/incoming/:webhookId/:token/maple.
  • rpc/channel-webhooks.ts — extend the integrationProvider literal on channelWebhook.create to accept "maple" so the org-scoped integration-bot reuse path applies.

Backend

  • routes/incoming-webhooks.http.tsexecuteMaple handler: token validation (timing-safe), IntegrationBotService.getOrCreateWebhookBotUser(\"maple\", ...), embed-only message + outbox event + lastUsedAt update. Identical shape to executeRailway.

Web

  • lib/integrations/__data.ts — Maple marketplace card (developer-tools, webhook connection type).
  • components/embeds/use-embed-theme.ts — register maple as an EmbedProvider.
  • routes/.../integrations/$integrationId.tsx — recognize \"maple\" as a webhook integration and route to the new content component.
  • New: components/integrations/maple-integration-content.tsx, configure-maple-modal.tsx, channel-settings/maple-section.tsx. Setup UX shows the generated …/maple URL with a copy button and instructions to paste it as a Hazel destination in Maple → Alerts → Destinations.

Reviewer notes

  • No DB migration. The integration uses the existing channel_webhooks table; bot identity is the shared per-org integration-bot-maple user via IntegrationBotService.
  • The companion Maple-side change (a dedicated \"hazel\" destination type that POSTs to /maple) is in a separate PR on the maple repo.
  • Token / signing: the URL token is validated as today (SHA-256 + timingSafeEqual). HMAC verification of x-maple-signature is not implemented yet — left as a follow-up since the URL token itself is already an authenticated bearer credential. Maple sends the header when a signing secret is configured; Hazel currently ignores it.
  • Idempotency: Hazel does not deduplicate by Maple's dedupeKey, so a retried delivery from Maple will produce a duplicate channel message. Acceptable for now; can be added later via a uniqueness index keyed on dedupeKey.

Test plan

  • bun run typecheck passes (verified locally — 18/18 packages).
  • In a running Hazel web app, navigate to /{org}/settings/integrations/maple → confirm the marketplace card renders with the Maple icon.
  • Click Add Channel, pick a test channel, copy the generated …/maple webhook URL.
  • On the Maple side (companion PR), create a Hazel destination with that URL and hit Test → confirm a Maple-branded test embed appears in the chosen Hazel channel with severity badge and "Open in Maple" link.
  • Trigger a real rule with the Hazel destination attached → confirm both trigger (red/amber) and resolve (green) embeds render correctly.
  • Disable then re-enable the webhook from the channel section → confirm webhooks toggle without data loss.
  • Delete the webhook → confirm the bot user persists (org-scoped reuse) but the channel webhook is removed.

🤖 Generated with Claude Code

Adds Maple as a new webhook-based integration that posts alert events
into a Hazel channel as rich embeds, following the same pattern as the
existing Railway and OpenStatus integrations.

- Bot registry: register "maple" as a WebhookProvider with maple.dev
  branding so the per-org bot identity gets the right name and avatar
- Domain: MaplePayload schema mirroring Maple's existing buildPayload
  output (eventType, rule, observed, linkUrl, chatUrl, …) plus a new
  executeMaple endpoint at /webhooks/incoming/:webhookId/:token/maple
- Backend handler: token validation, Maple bot identity resolution,
  embed-only message creation, outbox event, lastUsedAt update
- @hazel/integrations/maple module: payload types, severity-aware
  colors, and a buildMapleEmbed that renders trigger / resolve /
  renotify / test events with badge, fields, and deep link
- channelWebhook.create RPC: extend integrationProvider literal with
  "maple" so the org-scoped bot reuse path works
- Web: marketplace card, integration content + setup modal, and the
  per-channel section component (mirrors openstatus)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hazel-docs Ready Ready Preview, Comment Apr 26, 2026 11:39am

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 26, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
hazel-app 39bc1f5 Commit Preview URL

Branch Preview URL
Apr 26 2026, 11:40 AM

@claude
Copy link
Copy Markdown

claude Bot commented Apr 26, 2026

Claude finished @Makisuo's task —— View job


PR Review: Maple Alerts Integration

I've completed a thorough review of this Maple alerts integration PR. Overall, this is high-quality code that follows excellent patterns and closely mirrors the existing OpenStatus/Railway integrations. Here's my detailed analysis:

Todo List:

  • Review backend webhook handler implementation
  • Check frontend components and integration setup
  • Evaluate security considerations
  • Assess adherence to project patterns
  • Check for potential bugs and issues
  • Review performance implications
  • Evaluate test coverage

Strengths & Best Practices

Backend Implementation

  • Excellent pattern adherence: The executeMaple handler follows existing webhook patterns perfectly
  • Strong security: Proper timing-safe token validation using timingSafeEqual
  • Robust architecture: Transaction-wrapped message creation with outbox pattern for eventual consistency
  • Good error handling: Uses structured Effect error types with proper HTTP status codes
  • Performance optimized: Fire-and-forget pattern for updateLastUsed to avoid blocking

Frontend Implementation

  • Clean component structure: Well-organized React components with proper separation of concerns
  • Good UX patterns: Follows existing integration setup flows with clear success states
  • Proper state management: Uses Effect Atom patterns consistently
  • Accessible UI: Proper ARIA labels and keyboard navigation

Integration Package

  • Well-designed schemas: TypeScript interfaces cleanly mirror Maple's payload structure
  • Smart color mapping: Severity-aware styling with proper color codes
  • Rich embeds: Comprehensive embed builder with proper field formatting
  • Clean exports: Proper browser/server separation with index/browser entry points

⚠️ Areas for Improvement

1. Code Duplication (Medium Priority)

The three webhook handlers (executeOpenStatus, executeRailway, executeMaple) share ~90% identical code. Consider extracting shared logic:

Location: apps/backend/src/routes/incoming-webhooks.http.ts:371-463

Suggestion: Extract a shared handler function:

const executeWebhookHandler = <T>(
  provider: WebhookProvider,
  buildEmbed: (payload: T) => MessageEmbed
) => ({ params, payload }: { params: { webhookId: string; token: string }; payload: T }) =>
  Effect.gen(function* () {
    // Common validation logic...
    const botUser = yield* botService.getOrCreateWebhookBotUser(provider, webhook.organizationId)
    const embed = buildEmbed(payload)
    // Common message creation logic...
  })

This would reduce maintenance burden and ensure consistency across webhook handlers.

2. Missing HMAC Verification (Low Priority)

Location: Backend webhook handler

The PR description notes that HMAC verification via x-maple-signature is not implemented yet. While the URL token provides authentication, HMAC verification is a security best practice for webhooks:

// Future enhancement suggestion:
const verifyMapleSignature = (payload: string, signature: string, secret: string) => {
  const expectedSignature = createHmac('sha256', secret).update(payload).digest('hex')
  return timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))
}

3. No Idempotency Handling (Low Priority)

Location: Backend webhook handler

The PR description mentions that Maple sends a dedupeKey, but Hazel doesn't deduplicate. While acceptable for now, consider adding a unique index on dedupeKey for the future to prevent duplicate messages on retry.

4. Minor: Missing Null Safety

Location: packages/integrations/src/maple/embed-builder.ts:58

// Current - potential null issue:
const observedValue = `${formatNumber(observed.value)} (threshold ${COMPARATOR_LABEL[rule.comparator]} ${formatNumber(rule.threshold)})`

// Suggestion - add null check:
if (observed.value !== null && observed.value !== undefined) {
  const observedValue = `${formatNumber(observed.value)} (threshold ${COMPARATOR_LABEL[rule.comparator]} ${formatNumber(rule.threshold)})`
  // ...
}

🔒 Security Assessment

✅ Strong Security Posture:

  • Timing-safe token comparison prevents timing attacks
  • URL tokens are properly hashed with SHA-256
  • No direct SQL injection vectors (using Drizzle ORM)
  • Proper input validation via Effect schemas
  • No credential leakage in logs

Future considerations:

  • Add HMAC signature verification for defense-in-depth
  • Consider rate limiting for webhook endpoints

🚀 Performance & Scalability

✅ Well Optimized:

  • Minimal database queries (single transaction)
  • Fire-and-forget updates don't block responses
  • Proper Effect error handling avoids unnecessary computation
  • Clean embed building with minimal string operations

No performance concerns identified.


🧪 Test Coverage

⚠️ Missing Tests:
The PR doesn't include tests for the new Maple integration. Consider adding:

  1. Unit tests for buildMapleEmbed function with different event types
  2. Integration tests for the webhook endpoint
  3. Frontend component tests for the Maple integration setup flow

Example test structure:

// packages/integrations/src/maple/__tests__/embed-builder.test.ts
describe('buildMapleEmbed', () => {
  it('should build trigger embed with critical severity', () => {
    // Test critical trigger event
  })
  
  it('should build resolve embed with success styling', () => {
    // Test resolve event
  })
})

📋 Final Recommendations

Required Before Merge

  • Run typecheck: Verify bun run typecheck passes (mentioned as verified locally)
  • Add basic tests: At minimum, unit tests for embed builder

Nice to Have

  • Extract shared webhook handler: Reduce code duplication
  • Add integration tests: Test the full webhook flow
  • Document HMAC verification: Plan for future security enhancement

🎯 Overall Assessment

This is excellent work that demonstrates strong engineering practices:

  • ✅ Follows established patterns perfectly
  • ✅ Strong security implementation
  • ✅ Clean, maintainable code structure
  • ✅ Comprehensive feature implementation
  • ✅ Good documentation in PR description

The integration is production-ready with only minor improvements suggested. The code quality is consistent with the rest of the Hazel codebase and properly implements all the expected functionality.

Recommendation: APPROVE with the minor suggestions addressed in follow-up PRs.


Job Run

@Makisuo Makisuo merged commit 124ec60 into main Apr 26, 2026
7 of 9 checks passed
@Makisuo Makisuo deleted the feat/maple-alerts-integration branch April 26, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant