Files
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

177 lines
8.7 KiB
TypeScript

import type { SubBlockConfig } from '@/blocks/types'
import type { TriggerOutput } from '@/triggers/types'
/**
* Shared trigger dropdown options for all Rootly triggers.
* IDs map to Rootly webhook event type strings via {@link ROOTLY_EVENT_TYPE_MAP}.
*/
export const rootlyTriggerOptions = [
{ label: 'Incident Created', id: 'rootly_incident_created' },
{ label: 'Incident Updated', id: 'rootly_incident_updated' },
{ label: 'Incident Resolved', id: 'rootly_incident_resolved' },
{ label: 'Alert Created', id: 'rootly_alert_created' },
]
/**
* Maps a Sim trigger ID to the exact Rootly webhook event type string.
* Source: https://docs.rootly.com/configuration/webhooks
*/
export const ROOTLY_EVENT_TYPE_MAP: Record<string, string> = {
rootly_incident_created: 'incident.created',
rootly_incident_updated: 'incident.updated',
rootly_incident_resolved: 'incident.resolved',
rootly_alert_created: 'alert.created',
}
/**
* Generate setup instructions for a specific Rootly event type.
* Rootly webhook endpoints are created and torn down programmatically via the
* Rootly API, so the user only needs to provide an API key — Sim registers the
* endpoint and manages its signing secret automatically.
*/
export function rootlySetupInstructions(eventType: string): string {
const instructions = [
'In Rootly, go to <strong>Settings &gt; API Keys</strong> and generate (or copy) an API key.',
'Paste the API key into the <strong>API Key</strong> field below.',
`When you save, Sim creates a Rootly webhook endpoint subscribed to the <strong>${eventType}</strong> event and verifies request signatures automatically — no manual webhook setup needed.`,
'Click "Save" above to activate your trigger.',
]
return instructions
.map(
(instruction, index) =>
`<div class="mb-3"><strong>${index + 1}.</strong> ${instruction}</div>`
)
.join('')
}
/**
* Resolve the Rootly webhook `event_types` array to subscribe an endpoint to,
* based on the configured Sim trigger. Unknown/generic trigger IDs subscribe to
* every supported event type.
*/
export function rootlyEventTypesForTrigger(triggerId: string | undefined): string[] {
const mapped = triggerId ? ROOTLY_EVENT_TYPE_MAP[triggerId] : undefined
if (mapped) return [mapped]
return Object.values(ROOTLY_EVENT_TYPE_MAP)
}
/**
* Extra config fields for Rootly triggers. The Rootly webhook endpoint is
* registered via API, so the user supplies an API key (masked) instead of
* pasting a signing secret — Sim generates and stores the secret itself.
*/
export function buildRootlyExtraFields(triggerId: string): SubBlockConfig[] {
return [
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Rootly API key',
password: true,
required: true,
paramVisibility: 'user-only',
mode: 'trigger',
condition: { field: 'selectedTriggerId', value: triggerId },
},
]
}
/**
* Output schema for Rootly incident events (incident.created/updated/resolved).
* `data` mirrors the documented Rootly incident resource. Field names are taken
* verbatim from https://docs.rootly.com/configuration/event-payloads.
*/
export function buildRootlyIncidentOutputs(): Record<string, TriggerOutput> {
return {
eventId: { type: 'string', description: 'Unique webhook event ID' },
eventType: { type: 'string', description: 'Rootly event type (e.g. incident.created)' },
issuedAt: { type: 'string', description: 'When the event was issued (ISO 8601)' },
data: {
id: { type: 'string', description: 'Incident ID' },
sequential_id: { type: 'number', description: 'Sequential incident number' },
title: { type: 'string', description: 'Incident title' },
public_title: { type: 'string', description: 'Public-facing incident title' },
slug: { type: 'string', description: 'Incident slug' },
kind: { type: 'string', description: 'Incident kind (normal, test, etc.)' },
private: { type: 'boolean', description: 'Whether the incident is private' },
summary: { type: 'string', description: 'Incident summary' },
status: { type: 'string', description: 'Incident status' },
url: { type: 'string', description: 'Incident URL in Rootly' },
short_url: { type: 'string', description: 'Shortened incident URL' },
mitigation_message: { type: 'string', description: 'Mitigation message' },
resolution_message: { type: 'string', description: 'Resolution message' },
cancellation_message: { type: 'string', description: 'Cancellation message' },
slack_channel_name: { type: 'string', description: 'Linked Slack channel name' },
slack_channel_id: { type: 'string', description: 'Linked Slack channel ID' },
slack_channel_url: { type: 'string', description: 'Linked Slack channel URL' },
started_at: { type: 'string', description: 'When the incident started' },
detected_at: { type: 'string', description: 'When the incident was detected' },
acknowledged_at: { type: 'string', description: 'When the incident was acknowledged' },
mitigated_at: { type: 'string', description: 'When the incident was mitigated' },
resolved_at: { type: 'string', description: 'When the incident was resolved' },
cancelled_at: { type: 'string', description: 'When the incident was cancelled' },
created_at: { type: 'string', description: 'Incident creation timestamp' },
updated_at: { type: 'string', description: 'Incident last update timestamp' },
labels: { type: 'json', description: 'Incident labels (key-value pairs)' },
severity: { type: 'json', description: 'Incident severity object' },
user: { type: 'json', description: 'User who owns the incident' },
started_by: { type: 'json', description: 'User who started the incident' },
mitigated_by: { type: 'json', description: 'User who mitigated the incident' },
resolved_by: { type: 'json', description: 'User who resolved the incident' },
cancelled_by: { type: 'json', description: 'User who cancelled the incident' },
roles: { type: 'json', description: 'Assigned incident roles' },
environments: { type: 'json', description: 'Affected environments' },
incident_types: { type: 'json', description: 'Incident types' },
services: { type: 'json', description: 'Affected services' },
functionalities: { type: 'json', description: 'Affected functionalities' },
groups: { type: 'json', description: 'Associated teams/groups' },
events: { type: 'json', description: 'Timeline events' },
action_items: { type: 'json', description: 'Action items' },
incident_post_mortem: { type: 'json', description: 'Retrospective/post-mortem object' },
},
}
}
/**
* Output schema for Rootly alert events (alert.created).
* Field names are taken verbatim from the documented Rootly alert resource:
* https://docs.rootly.com/configuration/event-payloads.
*/
export function buildRootlyAlertOutputs(): Record<string, TriggerOutput> {
return {
eventId: { type: 'string', description: 'Unique webhook event ID' },
eventType: { type: 'string', description: 'Rootly event type (e.g. alert.created)' },
issuedAt: { type: 'string', description: 'When the event was issued (ISO 8601)' },
data: {
id: { type: 'string', description: 'Alert ID' },
team_id: { type: 'number', description: 'Team ID' },
source: { type: 'string', description: 'Alert source (e.g. pagerduty)' },
summary: { type: 'string', description: 'Alert summary' },
labels: { type: 'json', description: 'Alert labels' },
data: { type: 'json', description: 'Raw alert payload data' },
external_id: { type: 'string', description: 'External alert ID' },
external_url: { type: 'string', description: 'External alert URL' },
webhook_type: { type: 'string', description: 'Webhook type' },
webhook_id: { type: 'string', description: 'Webhook ID' },
webhook_idempotency_key: { type: 'string', description: 'Webhook idempotency key' },
started_at: { type: 'string', description: 'When the alert started' },
ended_at: { type: 'string', description: 'When the alert ended' },
deleted_at: { type: 'string', description: 'When the alert was deleted' },
created_at: { type: 'string', description: 'Alert creation timestamp' },
updated_at: { type: 'string', description: 'Alert last update timestamp' },
},
}
}
/**
* Check whether an incoming Rootly event type matches the configured trigger.
* Unknown trigger IDs are allowed through.
*/
export function isRootlyEventMatch(triggerId: string, eventType: string): boolean {
const expected = ROOTLY_EVENT_TYPE_MAP[triggerId]
if (!expected) {
return true
}
return expected === eventType
}