diff --git a/apps/docs/content/docs/en/tools/rootly.mdx b/apps/docs/content/docs/en/tools/rootly.mdx index 58b3cb1e90..d4574acc28 100644 --- a/apps/docs/content/docs/en/tools/rootly.mdx +++ b/apps/docs/content/docs/en/tools/rootly.mdx @@ -245,6 +245,7 @@ Create a new alert in Rootly for on-call notification and routing. | --------- | ---- | ----------- | | `alert` | object | The created alert | | ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | | ↳ `summary` | string | Alert summary | | ↳ `description` | string | Alert description | | ↳ `source` | string | Alert source | @@ -254,6 +255,8 @@ Create a new alert in Rootly for on-call notification and routing. | ↳ `deduplicationKey` | string | Deduplication key | | ↳ `createdAt` | string | Creation date | | ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | ### `rootly_list_alerts` @@ -278,6 +281,7 @@ List alerts from Rootly with optional filtering by status, source, and services. | --------- | ---- | ----------- | | `alerts` | array | List of alerts | | ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | | ↳ `summary` | string | Alert summary | | ↳ `description` | string | Alert description | | ↳ `source` | string | Alert source | @@ -287,6 +291,8 @@ List alerts from Rootly with optional filtering by status, source, and services. | ↳ `deduplicationKey` | string | Deduplication key | | ↳ `createdAt` | string | Creation date | | ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | | `totalCount` | number | Total number of alerts returned | ### `rootly_add_incident_event` @@ -507,4 +513,379 @@ List incident retrospectives (post-mortems) from Rootly. | ↳ `updatedAt` | string | Last update date | | `totalCount` | number | Total number of retrospectives returned | +### `rootly_delete_incident` + +Delete an incident by ID from Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to delete | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `success` | boolean | Whether the deletion succeeded | +| `message` | string | Result message | + +### `rootly_get_alert` + +Retrieve a single alert by ID from Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `alertId` | string | Yes | The ID of the alert to retrieve | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alert` | object | The alert details | +| ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | + +### `rootly_update_alert` + +Update an existing alert in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `alertId` | string | Yes | The ID of the alert to update | +| `summary` | string | No | Updated alert summary | +| `description` | string | No | Updated alert description | +| `source` | string | No | Updated alert source | +| `serviceIds` | string | No | Comma-separated service IDs to attach | +| `groupIds` | string | No | Comma-separated team/group IDs to attach | +| `environmentIds` | string | No | Comma-separated environment IDs to attach | +| `externalId` | string | No | Updated external ID | +| `externalUrl` | string | No | Updated external URL | +| `deduplicationKey` | string | No | Updated deduplication key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alert` | object | The updated alert | +| ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | + +### `rootly_acknowledge_alert` + +Acknowledge an alert in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `alertId` | string | Yes | The ID of the alert to acknowledge | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alert` | object | The acknowledged alert | +| ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | + +### `rootly_resolve_alert` + +Resolve an alert in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `alertId` | string | Yes | The ID of the alert to resolve | +| `resolutionMessage` | string | No | Message describing how the alert was resolved | +| `resolveRelatedIncidents` | boolean | No | Whether to also resolve related incidents | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `alert` | object | The resolved alert | +| ↳ `id` | string | Unique alert ID | +| ↳ `shortId` | string | Short alert ID | +| ↳ `summary` | string | Alert summary | +| ↳ `description` | string | Alert description | +| ↳ `source` | string | Alert source | +| ↳ `status` | string | Alert status | +| ↳ `externalId` | string | External ID | +| ↳ `externalUrl` | string | External URL | +| ↳ `deduplicationKey` | string | Deduplication key | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| ↳ `startedAt` | string | Start date | +| ↳ `endedAt` | string | End date | + +### `rootly_create_action_item` + +Create a new action item for an incident in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to add the action item to | +| `summary` | string | Yes | The title of the action item | +| `description` | string | No | A detailed description of the action item | +| `kind` | string | No | The kind of action item \(task, follow_up\) | +| `priority` | string | No | Priority level \(high, medium, low\) | +| `status` | string | No | Action item status \(open, in_progress, cancelled, done\) | +| `assignedToUserId` | string | No | The user ID to assign the action item to | +| `dueDate` | string | No | Due date for the action item | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `actionItem` | object | The created action item | +| ↳ `id` | string | Unique action item ID | +| ↳ `summary` | string | Action item title | +| ↳ `description` | string | Action item description | +| ↳ `kind` | string | Action item kind \(task, follow_up\) | +| ↳ `priority` | string | Priority level | +| ↳ `status` | string | Action item status | +| ↳ `dueDate` | string | Due date | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | + +### `rootly_list_action_items` + +List action items for an incident in Rootly. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `incidentId` | string | Yes | The ID of the incident to list action items for | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `actionItems` | array | List of action items | +| ↳ `id` | string | Unique action item ID | +| ↳ `summary` | string | Action item title | +| ↳ `description` | string | Action item description | +| ↳ `kind` | string | Action item kind \(task, follow_up\) | +| ↳ `priority` | string | Priority level | +| ↳ `status` | string | Action item status | +| ↳ `dueDate` | string | Due date | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of action items returned | + +### `rootly_list_users` + +List users from Rootly with optional search and email filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter users | +| `email` | string | No | Filter users by email address | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `users` | array | List of users | +| ↳ `id` | string | Unique user ID | +| ↳ `email` | string | User email address | +| ↳ `firstName` | string | User first name | +| ↳ `lastName` | string | User last name | +| ↳ `fullName` | string | User full name | +| ↳ `timeZone` | string | User time zone | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of users returned | + +### `rootly_list_on_calls` + +List current on-call entries from Rootly with optional filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `scheduleIds` | string | No | Comma-separated schedule IDs to filter by | +| `escalationPolicyIds` | string | No | Comma-separated escalation policy IDs to filter by | +| `userIds` | string | No | Comma-separated user IDs to filter by | +| `serviceIds` | string | No | Comma-separated service IDs to filter by | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `onCalls` | array | List of on-call entries | +| ↳ `id` | string | Unique on-call entry ID | +| ↳ `userId` | string | ID of the on-call user | +| ↳ `userName` | string | Name of the on-call user | +| ↳ `scheduleId` | string | ID of the associated schedule | +| ↳ `scheduleName` | string | Name of the associated schedule | +| ↳ `escalationPolicyId` | string | ID of the associated escalation policy | +| ↳ `startTime` | string | On-call start time | +| ↳ `endTime` | string | On-call end time | +| `totalCount` | number | Total number of on-call entries returned | + +### `rootly_list_schedules` + +List on-call schedules from Rootly with optional search filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter schedules | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `schedules` | array | List of schedules | +| ↳ `id` | string | Unique schedule ID | +| ↳ `name` | string | Schedule name | +| ↳ `description` | string | Schedule description | +| ↳ `allTimeCoverage` | boolean | Whether schedule provides 24/7 coverage | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of schedules returned | + +### `rootly_list_escalation_policies` + +List escalation policies from Rootly with optional search filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter escalation policies | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `escalationPolicies` | array | List of escalation policies | +| ↳ `id` | string | Unique escalation policy ID | +| ↳ `name` | string | Escalation policy name | +| ↳ `description` | string | Escalation policy description | +| ↳ `repeatCount` | number | Number of times to repeat escalation | +| ↳ `groupIds` | array | Associated group IDs | +| ↳ `serviceIds` | array | Associated service IDs | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of escalation policies returned | + +### `rootly_list_causes` + +List causes from Rootly with optional search filtering. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `search` | string | No | Search term to filter causes | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `causes` | array | List of causes | +| ↳ `id` | string | Unique cause ID | +| ↳ `name` | string | Cause name | +| ↳ `slug` | string | Cause slug | +| ↳ `description` | string | Cause description | +| ↳ `position` | number | Cause position | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of causes returned | + +### `rootly_list_playbooks` + +List playbooks from Rootly with pagination support. + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Rootly API key | +| `pageSize` | number | No | Number of items per page \(default: 20\) | +| `pageNumber` | number | No | Page number for pagination | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `playbooks` | array | List of playbooks | +| ↳ `id` | string | Unique playbook ID | +| ↳ `title` | string | Playbook title | +| ↳ `summary` | string | Playbook summary | +| ↳ `externalUrl` | string | External URL | +| ↳ `createdAt` | string | Creation date | +| ↳ `updatedAt` | string | Last update date | +| `totalCount` | number | Total number of playbooks returned | + diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index 3b578762f4..f83740fce5 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -9717,9 +9717,61 @@ { "name": "List Retrospectives", "description": "List incident retrospectives (post-mortems) from Rootly." + }, + { + "name": "Delete Incident", + "description": "Delete an incident by ID from Rootly." + }, + { + "name": "Get Alert", + "description": "Retrieve a single alert by ID from Rootly." + }, + { + "name": "Update Alert", + "description": "Update an existing alert in Rootly." + }, + { + "name": "Acknowledge Alert", + "description": "Acknowledge an alert in Rootly." + }, + { + "name": "Resolve Alert", + "description": "Resolve an alert in Rootly." + }, + { + "name": "Create Action Item", + "description": "Create a new action item for an incident in Rootly." + }, + { + "name": "List Action Items", + "description": "List action items for an incident in Rootly." + }, + { + "name": "List Users", + "description": "List users from Rootly with optional search and email filtering." + }, + { + "name": "List On-Calls", + "description": "List current on-call entries from Rootly with optional filtering." + }, + { + "name": "List Schedules", + "description": "List on-call schedules from Rootly with optional search filtering." + }, + { + "name": "List Escalation Policies", + "description": "List escalation policies from Rootly with optional search filtering." + }, + { + "name": "List Causes", + "description": "List causes from Rootly with optional search filtering." + }, + { + "name": "List Playbooks", + "description": "List playbooks from Rootly with pagination support." } ], - "operationCount": 14, + "operationCount": 27, "triggers": [], "triggerCount": 0, "authType": "api-key", diff --git a/apps/sim/blocks/blocks/rootly.ts b/apps/sim/blocks/blocks/rootly.ts index c00c6c836b..6a4486aa1f 100644 --- a/apps/sim/blocks/blocks/rootly.ts +++ b/apps/sim/blocks/blocks/rootly.ts @@ -36,11 +36,23 @@ export const RootlyBlock: BlockConfig = { { label: 'List Incident Types', id: 'rootly_list_incident_types' }, { label: 'List Functionalities', id: 'rootly_list_functionalities' }, { label: 'List Retrospectives', id: 'rootly_list_retrospectives' }, + { label: 'Delete Incident', id: 'rootly_delete_incident' }, + { label: 'Get Alert', id: 'rootly_get_alert' }, + { label: 'Update Alert', id: 'rootly_update_alert' }, + { label: 'Acknowledge Alert', id: 'rootly_acknowledge_alert' }, + { label: 'Resolve Alert', id: 'rootly_resolve_alert' }, + { label: 'Create Action Item', id: 'rootly_create_action_item' }, + { label: 'List Action Items', id: 'rootly_list_action_items' }, + { label: 'List Users', id: 'rootly_list_users' }, + { label: 'List On-Calls', id: 'rootly_list_on_calls' }, + { label: 'List Schedules', id: 'rootly_list_schedules' }, + { label: 'List Escalation Policies', id: 'rootly_list_escalation_policies' }, + { label: 'List Causes', id: 'rootly_list_causes' }, + { label: 'List Playbooks', id: 'rootly_list_playbooks' }, ], value: () => 'rootly_create_incident', }, - // Create Incident fields { id: 'title', title: 'Title', @@ -170,7 +182,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // Get Incident fields { id: 'getIncidentId', title: 'Incident ID', @@ -180,7 +191,6 @@ export const RootlyBlock: BlockConfig = { required: { field: 'operation', value: 'rootly_get_incident' }, }, - // Update Incident fields { id: 'updateIncidentId', title: 'Incident ID', @@ -341,7 +351,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Incidents fields { id: 'listIncidentsStatus', title: 'Status Filter', @@ -434,7 +443,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // Create Alert fields { id: 'alertSummary', title: 'Summary', @@ -520,7 +528,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Alerts fields { id: 'listAlertsStatus', title: 'Status Filter', @@ -585,7 +592,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // Add Incident Event fields { id: 'eventIncidentId', title: 'Incident ID', @@ -616,7 +622,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Services fields { id: 'servicesSearch', title: 'Search', @@ -641,7 +646,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Severities fields { id: 'severitiesSearch', title: 'Search', @@ -666,7 +670,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Teams fields { id: 'teamsSearch', title: 'Search', @@ -691,7 +694,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Environments fields { id: 'environmentsSearch', title: 'Search', @@ -716,7 +718,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Incident Types fields { id: 'incidentTypesSearch', title: 'Name Filter', @@ -741,7 +742,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Functionalities fields { id: 'functionalitiesSearch', title: 'Search', @@ -766,7 +766,6 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // List Retrospectives fields { id: 'retrospectivesStatus', title: 'Status Filter', @@ -803,7 +802,404 @@ export const RootlyBlock: BlockConfig = { mode: 'advanced', }, - // API Key (common) + { + id: 'deleteIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident to delete', + condition: { field: 'operation', value: 'rootly_delete_incident' }, + required: { field: 'operation', value: 'rootly_delete_incident' }, + }, + + { + id: 'getAlertId', + title: 'Alert ID', + type: 'short-input', + placeholder: 'The ID of the alert to retrieve', + condition: { field: 'operation', value: 'rootly_get_alert' }, + required: { field: 'operation', value: 'rootly_get_alert' }, + }, + + { + id: 'updateAlertId', + title: 'Alert ID', + type: 'short-input', + placeholder: 'The ID of the alert to update', + condition: { field: 'operation', value: 'rootly_update_alert' }, + required: { field: 'operation', value: 'rootly_update_alert' }, + }, + { + id: 'updateAlertSummary', + title: 'Summary', + type: 'short-input', + placeholder: 'Updated alert summary', + condition: { field: 'operation', value: 'rootly_update_alert' }, + }, + { + id: 'updateAlertDescription', + title: 'Description', + type: 'long-input', + placeholder: 'Updated alert description', + condition: { field: 'operation', value: 'rootly_update_alert' }, + }, + { + id: 'updateAlertSource', + title: 'Source', + type: 'short-input', + placeholder: 'Alert source (e.g., api, datadog)', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertServiceIds', + title: 'Service IDs', + type: 'short-input', + placeholder: 'Comma-separated service IDs', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertGroupIds', + title: 'Team IDs', + type: 'short-input', + placeholder: 'Comma-separated team/group IDs', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertEnvironmentIds', + title: 'Environment IDs', + type: 'short-input', + placeholder: 'Comma-separated environment IDs', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertExternalId', + title: 'External ID', + type: 'short-input', + placeholder: 'External alert ID', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertExternalUrl', + title: 'External URL', + type: 'short-input', + placeholder: 'Link to external source', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + { + id: 'updateAlertDeduplicationKey', + title: 'Deduplication Key', + type: 'short-input', + placeholder: 'Key to deduplicate alerts', + condition: { field: 'operation', value: 'rootly_update_alert' }, + mode: 'advanced', + }, + + { + id: 'ackAlertId', + title: 'Alert ID', + type: 'short-input', + placeholder: 'The ID of the alert to acknowledge', + condition: { field: 'operation', value: 'rootly_acknowledge_alert' }, + required: { field: 'operation', value: 'rootly_acknowledge_alert' }, + }, + + { + id: 'resolveAlertId', + title: 'Alert ID', + type: 'short-input', + placeholder: 'The ID of the alert to resolve', + condition: { field: 'operation', value: 'rootly_resolve_alert' }, + required: { field: 'operation', value: 'rootly_resolve_alert' }, + }, + { + id: 'resolveResolutionMessage', + title: 'Resolution Message', + type: 'long-input', + placeholder: 'How was the alert resolved?', + condition: { field: 'operation', value: 'rootly_resolve_alert' }, + }, + { + id: 'resolveRelatedIncidents', + title: 'Resolve Related Incidents', + type: 'dropdown', + options: [ + { label: 'No', id: '' }, + { label: 'Yes', id: 'true' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_resolve_alert' }, + mode: 'advanced', + }, + + { + id: 'actionItemIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + required: { field: 'operation', value: 'rootly_create_action_item' }, + }, + { + id: 'actionItemSummary', + title: 'Summary', + type: 'short-input', + placeholder: 'Action item title', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + required: { field: 'operation', value: 'rootly_create_action_item' }, + }, + { + id: 'actionItemDescription', + title: 'Description', + type: 'long-input', + placeholder: 'Describe the action item', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + }, + { + id: 'actionItemKind', + title: 'Kind', + type: 'dropdown', + options: [ + { label: 'Default', id: '' }, + { label: 'Task', id: 'task' }, + { label: 'Follow Up', id: 'follow_up' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + }, + { + id: 'actionItemPriority', + title: 'Priority', + type: 'dropdown', + options: [ + { label: 'Default', id: '' }, + { label: 'High', id: 'high' }, + { label: 'Medium', id: 'medium' }, + { label: 'Low', id: 'low' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + }, + { + id: 'actionItemStatus', + title: 'Status', + type: 'dropdown', + options: [ + { label: 'Default', id: '' }, + { label: 'Open', id: 'open' }, + { label: 'In Progress', id: 'in_progress' }, + { label: 'Cancelled', id: 'cancelled' }, + { label: 'Done', id: 'done' }, + ], + value: () => '', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + mode: 'advanced', + }, + { + id: 'actionItemAssignedToUserId', + title: 'Assigned To User ID', + type: 'short-input', + placeholder: 'User ID to assign (use List Users to find IDs)', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + mode: 'advanced', + }, + { + id: 'actionItemDueDate', + title: 'Due Date', + type: 'short-input', + placeholder: 'YYYY-MM-DD', + condition: { field: 'operation', value: 'rootly_create_action_item' }, + mode: 'advanced', + wandConfig: { + enabled: true, + prompt: + 'Generate a date in YYYY-MM-DD format for the requested due date. Return ONLY the date string - no explanations, no extra text.', + placeholder: 'Describe the due date (e.g., "next Friday", "in 2 weeks")...', + generationType: 'timestamp', + }, + }, + + { + id: 'listActionItemsIncidentId', + title: 'Incident ID', + type: 'short-input', + placeholder: 'The ID of the incident', + condition: { field: 'operation', value: 'rootly_list_action_items' }, + required: { field: 'operation', value: 'rootly_list_action_items' }, + }, + { + id: 'listActionItemsPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_action_items' }, + mode: 'advanced', + }, + { + id: 'listActionItemsPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_action_items' }, + mode: 'advanced', + }, + + { + id: 'usersSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search users...', + condition: { field: 'operation', value: 'rootly_list_users' }, + }, + { + id: 'usersEmail', + title: 'Email Filter', + type: 'short-input', + placeholder: 'Filter by email address', + condition: { field: 'operation', value: 'rootly_list_users' }, + mode: 'advanced', + }, + { + id: 'usersPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_users' }, + mode: 'advanced', + }, + { + id: 'usersPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_users' }, + mode: 'advanced', + }, + + { + id: 'onCallsScheduleIds', + title: 'Schedule IDs', + type: 'short-input', + placeholder: 'Comma-separated schedule IDs', + condition: { field: 'operation', value: 'rootly_list_on_calls' }, + }, + { + id: 'onCallsEscalationPolicyIds', + title: 'Escalation Policy IDs', + type: 'short-input', + placeholder: 'Comma-separated escalation policy IDs', + condition: { field: 'operation', value: 'rootly_list_on_calls' }, + }, + { + id: 'onCallsUserIds', + title: 'User IDs', + type: 'short-input', + placeholder: 'Comma-separated user IDs', + condition: { field: 'operation', value: 'rootly_list_on_calls' }, + mode: 'advanced', + }, + { + id: 'onCallsServiceIds', + title: 'Service IDs', + type: 'short-input', + placeholder: 'Comma-separated service IDs', + condition: { field: 'operation', value: 'rootly_list_on_calls' }, + mode: 'advanced', + }, + + { + id: 'schedulesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search schedules...', + condition: { field: 'operation', value: 'rootly_list_schedules' }, + }, + { + id: 'schedulesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_schedules' }, + mode: 'advanced', + }, + { + id: 'schedulesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_schedules' }, + mode: 'advanced', + }, + + { + id: 'escalationPoliciesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search escalation policies...', + condition: { field: 'operation', value: 'rootly_list_escalation_policies' }, + }, + { + id: 'escalationPoliciesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_escalation_policies' }, + mode: 'advanced', + }, + { + id: 'escalationPoliciesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_escalation_policies' }, + mode: 'advanced', + }, + + { + id: 'causesSearch', + title: 'Search', + type: 'short-input', + placeholder: 'Search causes...', + condition: { field: 'operation', value: 'rootly_list_causes' }, + }, + { + id: 'causesPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_causes' }, + mode: 'advanced', + }, + { + id: 'causesPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_causes' }, + mode: 'advanced', + }, + + { + id: 'playbooksPageSize', + title: 'Page Size', + type: 'short-input', + placeholder: '20', + condition: { field: 'operation', value: 'rootly_list_playbooks' }, + mode: 'advanced', + }, + { + id: 'playbooksPageNumber', + title: 'Page Number', + type: 'short-input', + placeholder: '1', + condition: { field: 'operation', value: 'rootly_list_playbooks' }, + mode: 'advanced', + }, + { id: 'apiKey', title: 'API Key', @@ -829,6 +1225,19 @@ export const RootlyBlock: BlockConfig = { 'rootly_list_incident_types', 'rootly_list_functionalities', 'rootly_list_retrospectives', + 'rootly_delete_incident', + 'rootly_get_alert', + 'rootly_update_alert', + 'rootly_acknowledge_alert', + 'rootly_resolve_alert', + 'rootly_create_action_item', + 'rootly_list_action_items', + 'rootly_list_users', + 'rootly_list_on_calls', + 'rootly_list_schedules', + 'rootly_list_escalation_policies', + 'rootly_list_causes', + 'rootly_list_playbooks', ], config: { tool: (params) => params.operation, @@ -1012,6 +1421,131 @@ export const RootlyBlock: BlockConfig = { : undefined, } + case 'rootly_delete_incident': + return { + ...baseParams, + incidentId: params.deleteIncidentId, + } + + case 'rootly_get_alert': + return { + ...baseParams, + alertId: params.getAlertId, + } + + case 'rootly_update_alert': + return { + ...baseParams, + alertId: params.updateAlertId, + summary: params.updateAlertSummary, + description: params.updateAlertDescription, + source: params.updateAlertSource, + serviceIds: params.updateAlertServiceIds, + groupIds: params.updateAlertGroupIds, + environmentIds: params.updateAlertEnvironmentIds, + externalId: params.updateAlertExternalId, + externalUrl: params.updateAlertExternalUrl, + deduplicationKey: params.updateAlertDeduplicationKey, + } + + case 'rootly_acknowledge_alert': + return { + ...baseParams, + alertId: params.ackAlertId, + } + + case 'rootly_resolve_alert': + return { + ...baseParams, + alertId: params.resolveAlertId, + resolutionMessage: params.resolveResolutionMessage, + resolveRelatedIncidents: params.resolveRelatedIncidents + ? params.resolveRelatedIncidents === 'true' + : undefined, + } + + case 'rootly_create_action_item': + return { + ...baseParams, + incidentId: params.actionItemIncidentId, + summary: params.actionItemSummary, + description: params.actionItemDescription, + kind: params.actionItemKind, + priority: params.actionItemPriority, + status: params.actionItemStatus, + assignedToUserId: params.actionItemAssignedToUserId, + dueDate: params.actionItemDueDate, + } + + case 'rootly_list_action_items': + return { + ...baseParams, + incidentId: params.listActionItemsIncidentId, + pageSize: params.listActionItemsPageSize + ? Number(params.listActionItemsPageSize) + : undefined, + pageNumber: params.listActionItemsPageNumber + ? Number(params.listActionItemsPageNumber) + : undefined, + } + + case 'rootly_list_users': + return { + ...baseParams, + search: params.usersSearch, + email: params.usersEmail, + pageSize: params.usersPageSize ? Number(params.usersPageSize) : undefined, + pageNumber: params.usersPageNumber ? Number(params.usersPageNumber) : undefined, + } + + case 'rootly_list_on_calls': + return { + ...baseParams, + scheduleIds: params.onCallsScheduleIds, + escalationPolicyIds: params.onCallsEscalationPolicyIds, + userIds: params.onCallsUserIds, + serviceIds: params.onCallsServiceIds, + } + + case 'rootly_list_schedules': + return { + ...baseParams, + search: params.schedulesSearch, + pageSize: params.schedulesPageSize ? Number(params.schedulesPageSize) : undefined, + pageNumber: params.schedulesPageNumber + ? Number(params.schedulesPageNumber) + : undefined, + } + + case 'rootly_list_escalation_policies': + return { + ...baseParams, + search: params.escalationPoliciesSearch, + pageSize: params.escalationPoliciesPageSize + ? Number(params.escalationPoliciesPageSize) + : undefined, + pageNumber: params.escalationPoliciesPageNumber + ? Number(params.escalationPoliciesPageNumber) + : undefined, + } + + case 'rootly_list_causes': + return { + ...baseParams, + search: params.causesSearch, + pageSize: params.causesPageSize ? Number(params.causesPageSize) : undefined, + pageNumber: params.causesPageNumber ? Number(params.causesPageNumber) : undefined, + } + + case 'rootly_list_playbooks': + return { + ...baseParams, + pageSize: params.playbooksPageSize ? Number(params.playbooksPageSize) : undefined, + pageNumber: params.playbooksPageNumber + ? Number(params.playbooksPageNumber) + : undefined, + } + default: return baseParams } @@ -1101,6 +1635,58 @@ export const RootlyBlock: BlockConfig = { retrospectivesSearch: { type: 'string', description: 'Search retrospectives' }, retrospectivesPageSize: { type: 'string', description: 'Retrospectives page size' }, retrospectivesPageNumber: { type: 'string', description: 'Retrospectives page number' }, + deleteIncidentId: { type: 'string', description: 'Incident ID to delete' }, + getAlertId: { type: 'string', description: 'Alert ID to retrieve' }, + updateAlertId: { type: 'string', description: 'Alert ID to update' }, + updateAlertSummary: { type: 'string', description: 'Updated alert summary' }, + updateAlertDescription: { type: 'string', description: 'Updated alert description' }, + updateAlertSource: { type: 'string', description: 'Updated alert source' }, + updateAlertServiceIds: { type: 'string', description: 'Updated alert service IDs' }, + updateAlertGroupIds: { type: 'string', description: 'Updated alert team IDs' }, + updateAlertEnvironmentIds: { type: 'string', description: 'Updated alert environment IDs' }, + updateAlertExternalId: { type: 'string', description: 'Updated external alert ID' }, + updateAlertExternalUrl: { type: 'string', description: 'Updated external URL' }, + updateAlertDeduplicationKey: { type: 'string', description: 'Updated deduplication key' }, + ackAlertId: { type: 'string', description: 'Alert ID to acknowledge' }, + resolveAlertId: { type: 'string', description: 'Alert ID to resolve' }, + resolveResolutionMessage: { type: 'string', description: 'Resolution message' }, + resolveRelatedIncidents: { type: 'string', description: 'Resolve related incidents' }, + actionItemIncidentId: { type: 'string', description: 'Incident ID for action item' }, + actionItemSummary: { type: 'string', description: 'Action item summary' }, + actionItemDescription: { type: 'string', description: 'Action item description' }, + actionItemKind: { type: 'string', description: 'Action item kind' }, + actionItemPriority: { type: 'string', description: 'Action item priority' }, + actionItemStatus: { type: 'string', description: 'Action item status' }, + actionItemAssignedToUserId: { type: 'string', description: 'Assigned user ID' }, + actionItemDueDate: { type: 'string', description: 'Action item due date' }, + listActionItemsIncidentId: { type: 'string', description: 'Incident ID for action items' }, + listActionItemsPageSize: { type: 'string', description: 'Action items page size' }, + listActionItemsPageNumber: { type: 'string', description: 'Action items page number' }, + usersSearch: { type: 'string', description: 'Search users' }, + usersEmail: { type: 'string', description: 'Filter users by email' }, + usersPageSize: { type: 'string', description: 'Users page size' }, + usersPageNumber: { type: 'string', description: 'Users page number' }, + onCallsScheduleIds: { type: 'string', description: 'Filter on-calls by schedule IDs' }, + onCallsEscalationPolicyIds: { + type: 'string', + description: 'Filter on-calls by escalation policy IDs', + }, + onCallsUserIds: { type: 'string', description: 'Filter on-calls by user IDs' }, + onCallsServiceIds: { type: 'string', description: 'Filter on-calls by service IDs' }, + schedulesSearch: { type: 'string', description: 'Search schedules' }, + schedulesPageSize: { type: 'string', description: 'Schedules page size' }, + schedulesPageNumber: { type: 'string', description: 'Schedules page number' }, + escalationPoliciesSearch: { type: 'string', description: 'Search escalation policies' }, + escalationPoliciesPageSize: { type: 'string', description: 'Escalation policies page size' }, + escalationPoliciesPageNumber: { + type: 'string', + description: 'Escalation policies page number', + }, + causesSearch: { type: 'string', description: 'Search causes' }, + causesPageSize: { type: 'string', description: 'Causes page size' }, + causesPageNumber: { type: 'string', description: 'Causes page number' }, + playbooksPageSize: { type: 'string', description: 'Playbooks page size' }, + playbooksPageNumber: { type: 'string', description: 'Playbooks page number' }, }, outputs: { incident: { @@ -1150,6 +1736,42 @@ export const RootlyBlock: BlockConfig = { type: 'json', description: 'List of retrospectives (id, title, status, url, timestamps)', }, + users: { + type: 'json', + description: 'List of users (id, name, email)', + }, + onCalls: { + type: 'json', + description: + 'List of on-call entries (userId, userName, scheduleId, scheduleName, escalationPolicyId)', + }, + schedules: { + type: 'json', + description: 'List of schedules (id, name, description)', + }, + escalationPolicies: { + type: 'json', + description: 'List of escalation policies (id, name, description)', + }, + causes: { + type: 'json', + description: 'List of causes (id, name, slug, description)', + }, + playbooks: { + type: 'json', + description: 'List of playbooks (id, title, summary)', + }, + actionItem: { + type: 'json', + description: 'Action item data (id, summary, description, kind, priority, status, dueDate)', + }, + actionItems: { + type: 'json', + description: + 'List of action items (id, summary, description, kind, priority, status, dueDate)', + }, + success: { type: 'boolean', description: 'Whether the operation succeeded' }, + message: { type: 'string', description: 'Operation result message' }, totalCount: { type: 'number', description: 'Total count of items returned' }, }, } diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index eba06d416e..90445da862 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1986,19 +1986,32 @@ import { ripplingUpdateWorkLocationTool, } from '@/tools/rippling' import { + rootlyAcknowledgeAlertTool, rootlyAddIncidentEventTool, + rootlyCreateActionItemTool, rootlyCreateAlertTool, rootlyCreateIncidentTool, + rootlyDeleteIncidentTool, + rootlyGetAlertTool, rootlyGetIncidentTool, + rootlyListActionItemsTool, rootlyListAlertsTool, + rootlyListCausesTool, rootlyListEnvironmentsTool, + rootlyListEscalationPoliciesTool, rootlyListFunctionalitiesTool, rootlyListIncidentsTool, rootlyListIncidentTypesTool, + rootlyListOnCallsTool, + rootlyListPlaybooksTool, rootlyListRetrospectivesTool, + rootlyListSchedulesTool, rootlyListServicesTool, rootlyListSeveritiesTool, rootlyListTeamsTool, + rootlyListUsersTool, + rootlyResolveAlertTool, + rootlyUpdateAlertTool, rootlyUpdateIncidentTool, } from '@/tools/rootly' import { @@ -3790,19 +3803,32 @@ export const tools: Record = { rippling_update_supergroup_inclusion_members: ripplingUpdateSupergroupInclusionMembersTool, rippling_update_title: ripplingUpdateTitleTool, rippling_update_work_location: ripplingUpdateWorkLocationTool, + rootly_acknowledge_alert: rootlyAcknowledgeAlertTool, rootly_add_incident_event: rootlyAddIncidentEventTool, + rootly_create_action_item: rootlyCreateActionItemTool, rootly_create_alert: rootlyCreateAlertTool, rootly_create_incident: rootlyCreateIncidentTool, + rootly_delete_incident: rootlyDeleteIncidentTool, + rootly_get_alert: rootlyGetAlertTool, rootly_get_incident: rootlyGetIncidentTool, + rootly_list_action_items: rootlyListActionItemsTool, rootly_list_alerts: rootlyListAlertsTool, + rootly_list_causes: rootlyListCausesTool, rootly_list_environments: rootlyListEnvironmentsTool, + rootly_list_escalation_policies: rootlyListEscalationPoliciesTool, rootly_list_functionalities: rootlyListFunctionalitiesTool, rootly_list_incident_types: rootlyListIncidentTypesTool, rootly_list_incidents: rootlyListIncidentsTool, + rootly_list_on_calls: rootlyListOnCallsTool, + rootly_list_playbooks: rootlyListPlaybooksTool, rootly_list_retrospectives: rootlyListRetrospectivesTool, + rootly_list_schedules: rootlyListSchedulesTool, rootly_list_services: rootlyListServicesTool, rootly_list_severities: rootlyListSeveritiesTool, rootly_list_teams: rootlyListTeamsTool, + rootly_list_users: rootlyListUsersTool, + rootly_resolve_alert: rootlyResolveAlertTool, + rootly_update_alert: rootlyUpdateAlertTool, rootly_update_incident: rootlyUpdateIncidentTool, google_drive_copy: googleDriveCopyTool, google_drive_create_folder: googleDriveCreateFolderTool, diff --git a/apps/sim/tools/rootly/acknowledge_alert.ts b/apps/sim/tools/rootly/acknowledge_alert.ts new file mode 100644 index 0000000000..a403ad7299 --- /dev/null +++ b/apps/sim/tools/rootly/acknowledge_alert.ts @@ -0,0 +1,96 @@ +import type { + RootlyAcknowledgeAlertParams, + RootlyAcknowledgeAlertResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyAcknowledgeAlertTool: ToolConfig< + RootlyAcknowledgeAlertParams, + RootlyAcknowledgeAlertResponse +> = { + id: 'rootly_acknowledge_alert', + name: 'Rootly Acknowledge Alert', + description: 'Acknowledge an alert in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + alertId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the alert to acknowledge', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/acknowledge`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: () => ({ data: {} }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alert: {} as RootlyAcknowledgeAlertResponse['output']['alert'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + alert: { + id: data.data?.id ?? null, + shortId: attrs.short_id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + source: attrs.source ?? null, + status: attrs.status ?? null, + externalId: attrs.external_id ?? null, + externalUrl: attrs.external_url ?? null, + deduplicationKey: attrs.deduplication_key ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + endedAt: attrs.ended_at ?? null, + }, + }, + } + }, + + outputs: { + alert: { + type: 'object', + description: 'The acknowledged alert', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + shortId: { type: 'string', description: 'Short alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + endedAt: { type: 'string', description: 'End date' }, + }, + }, + }, +} diff --git a/apps/sim/tools/rootly/create_action_item.ts b/apps/sim/tools/rootly/create_action_item.ts new file mode 100644 index 0000000000..7297d19860 --- /dev/null +++ b/apps/sim/tools/rootly/create_action_item.ts @@ -0,0 +1,144 @@ +import type { + RootlyCreateActionItemParams, + RootlyCreateActionItemResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyCreateActionItemTool: ToolConfig< + RootlyCreateActionItemParams, + RootlyCreateActionItemResponse +> = { + id: 'rootly_create_action_item', + name: 'Rootly Create Action Item', + description: 'Create a new action item for an incident in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to add the action item to', + }, + summary: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The title of the action item', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'A detailed description of the action item', + }, + kind: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The kind of action item (task, follow_up)', + }, + priority: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Priority level (high, medium, low)', + }, + status: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Action item status (open, in_progress, cancelled, done)', + }, + assignedToUserId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'The user ID to assign the action item to', + }, + dueDate: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Due date for the action item', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = { + summary: params.summary, + } + if (params.description) attributes.description = params.description + if (params.kind) attributes.kind = params.kind + if (params.priority) attributes.priority = params.priority + if (params.status) attributes.status = params.status + if (params.assignedToUserId) { + const numericId = Number.parseInt(params.assignedToUserId, 10) + if (!Number.isNaN(numericId)) attributes.assigned_to_user_id = numericId + } + if (params.dueDate) attributes.due_date = params.dueDate + return { data: { type: 'incident_action_items', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { actionItem: {} as RootlyCreateActionItemResponse['output']['actionItem'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + actionItem: { + id: data.data?.id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + kind: attrs.kind ?? null, + priority: attrs.priority ?? null, + status: attrs.status ?? null, + dueDate: attrs.due_date ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + }, + }, + } + }, + + outputs: { + actionItem: { + type: 'object', + description: 'The created action item', + properties: { + id: { type: 'string', description: 'Unique action item ID' }, + summary: { type: 'string', description: 'Action item title' }, + description: { type: 'string', description: 'Action item description' }, + kind: { type: 'string', description: 'Action item kind (task, follow_up)' }, + priority: { type: 'string', description: 'Priority level' }, + status: { type: 'string', description: 'Action item status' }, + dueDate: { type: 'string', description: 'Due date' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, +} diff --git a/apps/sim/tools/rootly/create_alert.ts b/apps/sim/tools/rootly/create_alert.ts index 0687276f95..eaf5274309 100644 --- a/apps/sim/tools/rootly/create_alert.ts +++ b/apps/sim/tools/rootly/create_alert.ts @@ -124,6 +124,7 @@ export const rootlyCreateAlertTool: ToolConfig = { + id: 'rootly_delete_incident', + name: 'Rootly Delete Incident', + description: 'Delete an incident by ID from Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to delete', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}`, + method: 'DELETE', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { + success: false, + message: + errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + return { + success: true, + output: { + success: true, + message: 'Incident deleted successfully', + }, + } + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the deletion succeeded' }, + message: { type: 'string', description: 'Result message' }, + }, +} diff --git a/apps/sim/tools/rootly/get_alert.ts b/apps/sim/tools/rootly/get_alert.ts new file mode 100644 index 0000000000..3b148219fb --- /dev/null +++ b/apps/sim/tools/rootly/get_alert.ts @@ -0,0 +1,89 @@ +import type { RootlyGetAlertParams, RootlyGetAlertResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyGetAlertTool: ToolConfig = { + id: 'rootly_get_alert', + name: 'Rootly Get Alert', + description: 'Retrieve a single alert by ID from Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + alertId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the alert to retrieve', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alert: {} as RootlyGetAlertResponse['output']['alert'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + alert: { + id: data.data?.id ?? null, + shortId: attrs.short_id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + source: attrs.source ?? null, + status: attrs.status ?? null, + externalId: attrs.external_id ?? null, + externalUrl: attrs.external_url ?? null, + deduplicationKey: attrs.deduplication_key ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + endedAt: attrs.ended_at ?? null, + }, + }, + } + }, + + outputs: { + alert: { + type: 'object', + description: 'The alert details', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + shortId: { type: 'string', description: 'Short alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + endedAt: { type: 'string', description: 'End date' }, + }, + }, + }, +} diff --git a/apps/sim/tools/rootly/index.ts b/apps/sim/tools/rootly/index.ts index 53b2c4beb6..b3fff16b9c 100644 --- a/apps/sim/tools/rootly/index.ts +++ b/apps/sim/tools/rootly/index.ts @@ -1,15 +1,28 @@ +export { rootlyAcknowledgeAlertTool } from '@/tools/rootly/acknowledge_alert' export { rootlyAddIncidentEventTool } from '@/tools/rootly/add_incident_event' +export { rootlyCreateActionItemTool } from '@/tools/rootly/create_action_item' export { rootlyCreateAlertTool } from '@/tools/rootly/create_alert' export { rootlyCreateIncidentTool } from '@/tools/rootly/create_incident' +export { rootlyDeleteIncidentTool } from '@/tools/rootly/delete_incident' +export { rootlyGetAlertTool } from '@/tools/rootly/get_alert' export { rootlyGetIncidentTool } from '@/tools/rootly/get_incident' +export { rootlyListActionItemsTool } from '@/tools/rootly/list_action_items' export { rootlyListAlertsTool } from '@/tools/rootly/list_alerts' +export { rootlyListCausesTool } from '@/tools/rootly/list_causes' export { rootlyListEnvironmentsTool } from '@/tools/rootly/list_environments' +export { rootlyListEscalationPoliciesTool } from '@/tools/rootly/list_escalation_policies' export { rootlyListFunctionalitiesTool } from '@/tools/rootly/list_functionalities' export { rootlyListIncidentTypesTool } from '@/tools/rootly/list_incident_types' export { rootlyListIncidentsTool } from '@/tools/rootly/list_incidents' +export { rootlyListOnCallsTool } from '@/tools/rootly/list_on_calls' +export { rootlyListPlaybooksTool } from '@/tools/rootly/list_playbooks' export { rootlyListRetrospectivesTool } from '@/tools/rootly/list_retrospectives' +export { rootlyListSchedulesTool } from '@/tools/rootly/list_schedules' export { rootlyListServicesTool } from '@/tools/rootly/list_services' export { rootlyListSeveritiesTool } from '@/tools/rootly/list_severities' export { rootlyListTeamsTool } from '@/tools/rootly/list_teams' +export { rootlyListUsersTool } from '@/tools/rootly/list_users' +export { rootlyResolveAlertTool } from '@/tools/rootly/resolve_alert' export * from '@/tools/rootly/types' +export { rootlyUpdateAlertTool } from '@/tools/rootly/update_alert' export { rootlyUpdateIncidentTool } from '@/tools/rootly/update_incident' diff --git a/apps/sim/tools/rootly/list_action_items.ts b/apps/sim/tools/rootly/list_action_items.ts new file mode 100644 index 0000000000..96eb804c97 --- /dev/null +++ b/apps/sim/tools/rootly/list_action_items.ts @@ -0,0 +1,117 @@ +import type { + RootlyListActionItemsParams, + RootlyListActionItemsResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListActionItemsTool: ToolConfig< + RootlyListActionItemsParams, + RootlyListActionItemsResponse +> = { + id: 'rootly_list_action_items', + name: 'Rootly List Action Items', + description: 'List action items for an incident in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + incidentId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the incident to list action items for', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/incidents/${params.incidentId.trim()}/action_items${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { actionItems: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const actionItems = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + summary: (attrs.summary as string) ?? '', + description: (attrs.description as string) ?? null, + kind: (attrs.kind as string) ?? null, + priority: (attrs.priority as string) ?? null, + status: (attrs.status as string) ?? null, + dueDate: (attrs.due_date as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + actionItems, + totalCount: data.meta?.total_count ?? actionItems.length, + }, + } + }, + + outputs: { + actionItems: { + type: 'array', + description: 'List of action items', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique action item ID' }, + summary: { type: 'string', description: 'Action item title' }, + description: { type: 'string', description: 'Action item description' }, + kind: { type: 'string', description: 'Action item kind (task, follow_up)' }, + priority: { type: 'string', description: 'Priority level' }, + status: { type: 'string', description: 'Action item status' }, + dueDate: { type: 'string', description: 'Due date' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of action items returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_alerts.ts b/apps/sim/tools/rootly/list_alerts.ts index 6752799496..45e982a73a 100644 --- a/apps/sim/tools/rootly/list_alerts.ts +++ b/apps/sim/tools/rootly/list_alerts.ts @@ -93,6 +93,7 @@ export const rootlyListAlertsTool: ToolConfig return { id: item.id ?? null, + shortId: (attrs.short_id as string) ?? null, summary: (attrs.summary as string) ?? '', description: (attrs.description as string) ?? null, source: (attrs.source as string) ?? null, @@ -102,6 +103,8 @@ export const rootlyListAlertsTool: ToolConfig = { + id: 'rootly_list_causes', + name: 'Rootly List Causes', + description: 'List causes from Rootly with optional search filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter causes', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/causes${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { causes: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const causes = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + slug: (attrs.slug as string) ?? null, + description: (attrs.description as string) ?? null, + position: (attrs.position as number) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + causes, + totalCount: data.meta?.total_count ?? causes.length, + }, + } + }, + + outputs: { + causes: { + type: 'array', + description: 'List of causes', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique cause ID' }, + name: { type: 'string', description: 'Cause name' }, + slug: { type: 'string', description: 'Cause slug' }, + description: { type: 'string', description: 'Cause description' }, + position: { type: 'number', description: 'Cause position' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of causes returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_escalation_policies.ts b/apps/sim/tools/rootly/list_escalation_policies.ts new file mode 100644 index 0000000000..e62828b9bd --- /dev/null +++ b/apps/sim/tools/rootly/list_escalation_policies.ts @@ -0,0 +1,116 @@ +import type { + RootlyListEscalationPoliciesParams, + RootlyListEscalationPoliciesResponse, +} from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListEscalationPoliciesTool: ToolConfig< + RootlyListEscalationPoliciesParams, + RootlyListEscalationPoliciesResponse +> = { + id: 'rootly_list_escalation_policies', + name: 'Rootly List Escalation Policies', + description: 'List escalation policies from Rootly with optional search filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter escalation policies', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/escalation_policies${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { escalationPolicies: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const escalationPolicies = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + description: (attrs.description as string) ?? null, + repeatCount: (attrs.repeat_count as number) ?? null, + groupIds: (attrs.group_ids as string[]) ?? null, + serviceIds: (attrs.service_ids as string[]) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + escalationPolicies, + totalCount: data.meta?.total_count ?? escalationPolicies.length, + }, + } + }, + + outputs: { + escalationPolicies: { + type: 'array', + description: 'List of escalation policies', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique escalation policy ID' }, + name: { type: 'string', description: 'Escalation policy name' }, + description: { type: 'string', description: 'Escalation policy description' }, + repeatCount: { type: 'number', description: 'Number of times to repeat escalation' }, + groupIds: { type: 'array', description: 'Associated group IDs' }, + serviceIds: { type: 'array', description: 'Associated service IDs' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of escalation policies returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_on_calls.ts b/apps/sim/tools/rootly/list_on_calls.ts new file mode 100644 index 0000000000..ef6f3e287f --- /dev/null +++ b/apps/sim/tools/rootly/list_on_calls.ts @@ -0,0 +1,143 @@ +import type { RootlyListOnCallsParams, RootlyListOnCallsResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListOnCallsTool: ToolConfig = + { + id: 'rootly_list_on_calls', + name: 'Rootly List On-Calls', + description: 'List current on-call entries from Rootly with optional filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + scheduleIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated schedule IDs to filter by', + }, + escalationPolicyIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated escalation policy IDs to filter by', + }, + userIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated user IDs to filter by', + }, + serviceIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated service IDs to filter by', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.scheduleIds) queryParams.set('filter[schedule_ids]', params.scheduleIds) + if (params.escalationPolicyIds) + queryParams.set('filter[escalation_policy_ids]', params.escalationPolicyIds) + if (params.userIds) queryParams.set('filter[user_ids]', params.userIds) + if (params.serviceIds) queryParams.set('filter[service_ids]', params.serviceIds) + queryParams.set('include', 'user,schedule,escalation_policy') + return `https://api.rootly.com/v1/oncalls?${queryParams.toString()}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { onCalls: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const included = (data.included || []) as Record[] + const findIncluded = ( + type: string, + id: string | null + ): Record | undefined => + id ? included.find((i) => i.type === type && i.id === id) : undefined + + const onCalls = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + const rels = (item.relationships || {}) as Record> + + const userId = ((rels.user?.data as Record)?.id as string) ?? null + const scheduleId = ((rels.schedule?.data as Record)?.id as string) ?? null + const escalationPolicyId = + ((rels.escalation_policy?.data as Record)?.id as string) ?? null + + const userIncl = findIncluded('users', userId) + const scheduleIncl = findIncluded('schedules', scheduleId) + + return { + id: (item.id as string) ?? null, + userId, + userName: userIncl + ? (((userIncl.attributes as Record)?.full_name as string) ?? null) + : null, + scheduleId, + scheduleName: scheduleIncl + ? (((scheduleIncl.attributes as Record)?.name as string) ?? null) + : null, + escalationPolicyId, + startTime: (attrs.start_time as string) ?? null, + endTime: (attrs.end_time as string) ?? null, + } + }) + + return { + success: true, + output: { + onCalls, + totalCount: data.meta?.total_count ?? onCalls.length, + }, + } + }, + + outputs: { + onCalls: { + type: 'array', + description: 'List of on-call entries', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique on-call entry ID' }, + userId: { type: 'string', description: 'ID of the on-call user' }, + userName: { type: 'string', description: 'Name of the on-call user' }, + scheduleId: { type: 'string', description: 'ID of the associated schedule' }, + scheduleName: { type: 'string', description: 'Name of the associated schedule' }, + escalationPolicyId: { + type: 'string', + description: 'ID of the associated escalation policy', + }, + startTime: { type: 'string', description: 'On-call start time' }, + endTime: { type: 'string', description: 'On-call end time' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of on-call entries returned', + }, + }, + } diff --git a/apps/sim/tools/rootly/list_playbooks.ts b/apps/sim/tools/rootly/list_playbooks.ts new file mode 100644 index 0000000000..3abf4105dd --- /dev/null +++ b/apps/sim/tools/rootly/list_playbooks.ts @@ -0,0 +1,102 @@ +import type { RootlyListPlaybooksParams, RootlyListPlaybooksResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListPlaybooksTool: ToolConfig< + RootlyListPlaybooksParams, + RootlyListPlaybooksResponse +> = { + id: 'rootly_list_playbooks', + name: 'Rootly List Playbooks', + description: 'List playbooks from Rootly with pagination support.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/playbooks${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { playbooks: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const playbooks = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + title: (attrs.title as string) ?? '', + summary: (attrs.summary as string) ?? null, + externalUrl: (attrs.external_url as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + playbooks, + totalCount: data.meta?.total_count ?? playbooks.length, + }, + } + }, + + outputs: { + playbooks: { + type: 'array', + description: 'List of playbooks', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique playbook ID' }, + title: { type: 'string', description: 'Playbook title' }, + summary: { type: 'string', description: 'Playbook summary' }, + externalUrl: { type: 'string', description: 'External URL' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of playbooks returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_schedules.ts b/apps/sim/tools/rootly/list_schedules.ts new file mode 100644 index 0000000000..bcb57e349c --- /dev/null +++ b/apps/sim/tools/rootly/list_schedules.ts @@ -0,0 +1,112 @@ +import type { RootlyListSchedulesParams, RootlyListSchedulesResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListSchedulesTool: ToolConfig< + RootlyListSchedulesParams, + RootlyListSchedulesResponse +> = { + id: 'rootly_list_schedules', + name: 'Rootly List Schedules', + description: 'List on-call schedules from Rootly with optional search filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter schedules', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/schedules${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { schedules: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const schedules = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + name: (attrs.name as string) ?? '', + description: (attrs.description as string) ?? null, + allTimeCoverage: (attrs.all_time_coverage as boolean) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + schedules, + totalCount: data.meta?.total_count ?? schedules.length, + }, + } + }, + + outputs: { + schedules: { + type: 'array', + description: 'List of schedules', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique schedule ID' }, + name: { type: 'string', description: 'Schedule name' }, + description: { type: 'string', description: 'Schedule description' }, + allTimeCoverage: { + type: 'boolean', + description: 'Whether schedule provides 24/7 coverage', + }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of schedules returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/list_users.ts b/apps/sim/tools/rootly/list_users.ts new file mode 100644 index 0000000000..2e6c540739 --- /dev/null +++ b/apps/sim/tools/rootly/list_users.ts @@ -0,0 +1,117 @@ +import type { RootlyListUsersParams, RootlyListUsersResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyListUsersTool: ToolConfig = { + id: 'rootly_list_users', + name: 'Rootly List Users', + description: 'List users from Rootly with optional search and email filtering.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + search: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Search term to filter users', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Filter users by email address', + }, + pageSize: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of items per page (default: 20)', + }, + pageNumber: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Page number for pagination', + }, + }, + + request: { + url: (params) => { + const queryParams = new URLSearchParams() + if (params.search) queryParams.set('filter[search]', params.search) + if (params.email) queryParams.set('filter[email]', params.email) + if (params.pageSize) queryParams.set('page[size]', String(params.pageSize)) + if (params.pageNumber) queryParams.set('page[number]', String(params.pageNumber)) + const qs = queryParams.toString() + return `https://api.rootly.com/v1/users${qs ? `?${qs}` : ''}` + }, + method: 'GET', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { users: [], totalCount: 0 }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const users = (data.data || []).map((item: Record) => { + const attrs = (item.attributes || {}) as Record + return { + id: item.id ?? null, + email: (attrs.email as string) ?? '', + firstName: (attrs.first_name as string) ?? null, + lastName: (attrs.last_name as string) ?? null, + fullName: (attrs.full_name as string) ?? null, + timeZone: (attrs.time_zone as string) ?? null, + createdAt: (attrs.created_at as string) ?? '', + updatedAt: (attrs.updated_at as string) ?? '', + } + }) + + return { + success: true, + output: { + users, + totalCount: data.meta?.total_count ?? users.length, + }, + } + }, + + outputs: { + users: { + type: 'array', + description: 'List of users', + items: { + type: 'object', + properties: { + id: { type: 'string', description: 'Unique user ID' }, + email: { type: 'string', description: 'User email address' }, + firstName: { type: 'string', description: 'User first name' }, + lastName: { type: 'string', description: 'User last name' }, + fullName: { type: 'string', description: 'User full name' }, + timeZone: { type: 'string', description: 'User time zone' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + }, + }, + }, + totalCount: { + type: 'number', + description: 'Total number of users returned', + }, + }, +} diff --git a/apps/sim/tools/rootly/resolve_alert.ts b/apps/sim/tools/rootly/resolve_alert.ts new file mode 100644 index 0000000000..550dc61cee --- /dev/null +++ b/apps/sim/tools/rootly/resolve_alert.ts @@ -0,0 +1,112 @@ +import type { RootlyResolveAlertParams, RootlyResolveAlertResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyResolveAlertTool: ToolConfig< + RootlyResolveAlertParams, + RootlyResolveAlertResponse +> = { + id: 'rootly_resolve_alert', + name: 'Rootly Resolve Alert', + description: 'Resolve an alert in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + alertId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the alert to resolve', + }, + resolutionMessage: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Message describing how the alert was resolved', + }, + resolveRelatedIncidents: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to also resolve related incidents', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}/resolve`, + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = {} + if (params.resolutionMessage) attributes.resolution_message = params.resolutionMessage + if (params.resolveRelatedIncidents !== undefined) + attributes.resolve_related_incidents = params.resolveRelatedIncidents + if (Object.keys(attributes).length === 0) return { data: {} } + return { data: { type: 'alerts', attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alert: {} as RootlyResolveAlertResponse['output']['alert'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + alert: { + id: data.data?.id ?? null, + shortId: attrs.short_id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + source: attrs.source ?? null, + status: attrs.status ?? null, + externalId: attrs.external_id ?? null, + externalUrl: attrs.external_url ?? null, + deduplicationKey: attrs.deduplication_key ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + endedAt: attrs.ended_at ?? null, + }, + }, + } + }, + + outputs: { + alert: { + type: 'object', + description: 'The resolved alert', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + shortId: { type: 'string', description: 'Short alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + endedAt: { type: 'string', description: 'End date' }, + }, + }, + }, +} diff --git a/apps/sim/tools/rootly/types.ts b/apps/sim/tools/rootly/types.ts index 82dfd14149..72a92cf7ee 100644 --- a/apps/sim/tools/rootly/types.ts +++ b/apps/sim/tools/rootly/types.ts @@ -121,6 +121,7 @@ export interface RootlyCreateAlertParams extends RootlyBaseParams { export interface RootlyAlertData { id: string | null + shortId: string | null summary: string description: string | null source: string | null @@ -130,6 +131,8 @@ export interface RootlyAlertData { deduplicationKey: string | null createdAt: string updatedAt: string + startedAt: string | null + endedAt: string | null } export interface RootlyCreateAlertResponse extends ToolResponse { @@ -323,6 +326,109 @@ export interface RootlyListIncidentTypesResponse extends ToolResponse { } } +/** List Causes */ +export interface RootlyListCausesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyCauseData { + id: string | null + name: string + slug: string | null + description: string | null + position: number | null + createdAt: string + updatedAt: string +} + +export interface RootlyListCausesResponse extends ToolResponse { + output: { + causes: RootlyCauseData[] + totalCount: number + } +} + +/** List Playbooks */ +export interface RootlyListPlaybooksParams extends RootlyBaseParams { + pageSize?: number + pageNumber?: number +} + +export interface RootlyPlaybookData { + id: string | null + title: string + summary: string | null + externalUrl: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListPlaybooksResponse extends ToolResponse { + output: { + playbooks: RootlyPlaybookData[] + totalCount: number + } +} + +/** Delete Incident */ +export interface RootlyDeleteIncidentParams extends RootlyBaseParams { + incidentId: string +} + +export interface RootlyDeleteIncidentResponse extends ToolResponse { + output: { + success: boolean + message: string + } +} + +/** Action Item Data */ +export interface RootlyActionItemData { + id: string | null + summary: string + description: string | null + kind: string | null + priority: string | null + status: string | null + dueDate: string | null + createdAt: string + updatedAt: string +} + +/** Create Action Item */ +export interface RootlyCreateActionItemParams extends RootlyBaseParams { + incidentId: string + summary: string + description?: string + kind?: string + priority?: string + status?: string + assignedToUserId?: string + dueDate?: string +} + +export interface RootlyCreateActionItemResponse extends ToolResponse { + output: { + actionItem: RootlyActionItemData + } +} + +/** List Action Items */ +export interface RootlyListActionItemsParams extends RootlyBaseParams { + incidentId: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyListActionItemsResponse extends ToolResponse { + output: { + actionItems: RootlyActionItemData[] + totalCount: number + } +} + /** List Functionalities */ export interface RootlyListFunctionalitiesParams extends RootlyBaseParams { search?: string @@ -347,15 +453,177 @@ export interface RootlyListFunctionalitiesResponse extends ToolResponse { } } +/** Get Alert */ +export interface RootlyGetAlertParams extends RootlyBaseParams { + alertId: string +} + +export interface RootlyGetAlertResponse extends ToolResponse { + output: { + alert: RootlyAlertData + } +} + +/** Update Alert */ +export interface RootlyUpdateAlertParams extends RootlyBaseParams { + alertId: string + summary?: string + description?: string + source?: string + serviceIds?: string + groupIds?: string + environmentIds?: string + externalId?: string + externalUrl?: string + deduplicationKey?: string +} + +export interface RootlyUpdateAlertResponse extends ToolResponse { + output: { + alert: RootlyAlertData + } +} + +/** Acknowledge Alert */ +export interface RootlyAcknowledgeAlertParams extends RootlyBaseParams { + alertId: string +} + +export interface RootlyAcknowledgeAlertResponse extends ToolResponse { + output: { + alert: RootlyAlertData + } +} + +/** Resolve Alert */ +export interface RootlyResolveAlertParams extends RootlyBaseParams { + alertId: string + resolutionMessage?: string + resolveRelatedIncidents?: boolean +} + +export interface RootlyResolveAlertResponse extends ToolResponse { + output: { + alert: RootlyAlertData + } +} + +/** List Users */ +export interface RootlyListUsersParams extends RootlyBaseParams { + search?: string + email?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyUserData { + id: string | null + email: string + firstName: string | null + lastName: string | null + fullName: string | null + timeZone: string | null + createdAt: string + updatedAt: string +} + +export interface RootlyListUsersResponse extends ToolResponse { + output: { + users: RootlyUserData[] + totalCount: number + } +} + +/** List On-Calls */ +export interface RootlyListOnCallsParams extends RootlyBaseParams { + scheduleIds?: string + escalationPolicyIds?: string + userIds?: string + serviceIds?: string +} + +export interface RootlyOnCallData { + id: string | null + userId: string | null + userName: string | null + scheduleId: string | null + scheduleName: string | null + escalationPolicyId: string | null + startTime: string | null + endTime: string | null +} + +export interface RootlyListOnCallsResponse extends ToolResponse { + output: { + onCalls: RootlyOnCallData[] + totalCount: number + } +} + +/** List Schedules */ +export interface RootlyListSchedulesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyScheduleData { + id: string | null + name: string + description: string | null + allTimeCoverage: boolean | null + createdAt: string + updatedAt: string +} + +export interface RootlyListSchedulesResponse extends ToolResponse { + output: { + schedules: RootlyScheduleData[] + totalCount: number + } +} + +/** List Escalation Policies */ +export interface RootlyListEscalationPoliciesParams extends RootlyBaseParams { + search?: string + pageSize?: number + pageNumber?: number +} + +export interface RootlyEscalationPolicyData { + id: string | null + name: string + description: string | null + repeatCount: number | null + groupIds: string[] | null + serviceIds: string[] | null + createdAt: string + updatedAt: string +} + +export interface RootlyListEscalationPoliciesResponse extends ToolResponse { + output: { + escalationPolicies: RootlyEscalationPolicyData[] + totalCount: number + } +} + /** Union of all responses */ export type RootlyResponse = | RootlyCreateIncidentResponse | RootlyGetIncidentResponse | RootlyUpdateIncidentResponse + | RootlyDeleteIncidentResponse | RootlyListIncidentsResponse | RootlyCreateAlertResponse + | RootlyGetAlertResponse + | RootlyUpdateAlertResponse + | RootlyAcknowledgeAlertResponse + | RootlyResolveAlertResponse | RootlyListAlertsResponse | RootlyAddIncidentEventResponse + | RootlyCreateActionItemResponse + | RootlyListActionItemsResponse | RootlyListServicesResponse | RootlyListSeveritiesResponse | RootlyListRetrospectivesResponse @@ -363,3 +631,9 @@ export type RootlyResponse = | RootlyListEnvironmentsResponse | RootlyListIncidentTypesResponse | RootlyListFunctionalitiesResponse + | RootlyListCausesResponse + | RootlyListPlaybooksResponse + | RootlyListUsersResponse + | RootlyListOnCallsResponse + | RootlyListSchedulesResponse + | RootlyListEscalationPoliciesResponse diff --git a/apps/sim/tools/rootly/update_alert.ts b/apps/sim/tools/rootly/update_alert.ts new file mode 100644 index 0000000000..ac64a2dffd --- /dev/null +++ b/apps/sim/tools/rootly/update_alert.ts @@ -0,0 +1,163 @@ +import type { RootlyUpdateAlertParams, RootlyUpdateAlertResponse } from '@/tools/rootly/types' +import type { ToolConfig } from '@/tools/types' + +export const rootlyUpdateAlertTool: ToolConfig = + { + id: 'rootly_update_alert', + name: 'Rootly Update Alert', + description: 'Update an existing alert in Rootly.', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Rootly API key', + }, + alertId: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'The ID of the alert to update', + }, + summary: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated alert summary', + }, + description: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated alert description', + }, + source: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated alert source', + }, + serviceIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated service IDs to attach', + }, + groupIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated team/group IDs to attach', + }, + environmentIds: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Comma-separated environment IDs to attach', + }, + externalId: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated external ID', + }, + externalUrl: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated external URL', + }, + deduplicationKey: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Updated deduplication key', + }, + }, + + request: { + url: (params) => `https://api.rootly.com/v1/alerts/${params.alertId.trim()}`, + method: 'PATCH', + headers: (params) => ({ + 'Content-Type': 'application/vnd.api+json', + Authorization: `Bearer ${params.apiKey}`, + }), + body: (params) => { + const attributes: Record = {} + if (params.summary) attributes.summary = params.summary + if (params.description) attributes.description = params.description + if (params.source) attributes.source = params.source + if (params.externalId) attributes.external_id = params.externalId + if (params.externalUrl) attributes.external_url = params.externalUrl + if (params.deduplicationKey) attributes.deduplication_key = params.deduplicationKey + if (params.serviceIds) { + attributes.service_ids = params.serviceIds.split(',').map((s: string) => s.trim()) + } + if (params.groupIds) { + attributes.group_ids = params.groupIds.split(',').map((s: string) => s.trim()) + } + if (params.environmentIds) { + attributes.environment_ids = params.environmentIds.split(',').map((s: string) => s.trim()) + } + return { data: { type: 'alerts', id: params.alertId.trim(), attributes } } + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorData = await response.json().catch(() => ({})) + return { + success: false, + output: { alert: {} as RootlyUpdateAlertResponse['output']['alert'] }, + error: errorData.errors?.[0]?.detail || `HTTP ${response.status}: ${response.statusText}`, + } + } + + const data = await response.json() + const attrs = data.data?.attributes || {} + return { + success: true, + output: { + alert: { + id: data.data?.id ?? null, + shortId: attrs.short_id ?? null, + summary: attrs.summary ?? '', + description: attrs.description ?? null, + source: attrs.source ?? null, + status: attrs.status ?? null, + externalId: attrs.external_id ?? null, + externalUrl: attrs.external_url ?? null, + deduplicationKey: attrs.deduplication_key ?? null, + createdAt: attrs.created_at ?? '', + updatedAt: attrs.updated_at ?? '', + startedAt: attrs.started_at ?? null, + endedAt: attrs.ended_at ?? null, + }, + }, + } + }, + + outputs: { + alert: { + type: 'object', + description: 'The updated alert', + properties: { + id: { type: 'string', description: 'Unique alert ID' }, + shortId: { type: 'string', description: 'Short alert ID' }, + summary: { type: 'string', description: 'Alert summary' }, + description: { type: 'string', description: 'Alert description' }, + source: { type: 'string', description: 'Alert source' }, + status: { type: 'string', description: 'Alert status' }, + externalId: { type: 'string', description: 'External ID' }, + externalUrl: { type: 'string', description: 'External URL' }, + deduplicationKey: { type: 'string', description: 'Deduplication key' }, + createdAt: { type: 'string', description: 'Creation date' }, + updatedAt: { type: 'string', description: 'Last update date' }, + startedAt: { type: 'string', description: 'Start date' }, + endedAt: { type: 'string', description: 'End date' }, + }, + }, + }, + }