From c28a645c64183655aa4412dbab98e7386273d388 Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Thu, 3 Sep 2026 12:02:32 +0530 Subject: [PATCH 1/8] Add IncidentIO plugin for incident.io incident management Indexes Incidents, Alerts, Schedules, Teams and Users from the incident.io API, with dashboards for incident timelines/escalations, on-call schedules, and team/user perspectives. Co-Authored-By: Claude Sonnet 5 --- .github/CODEOWNERS | 1 + plugins/IncidentIO/v1/configValidation.json | 11 + plugins/IncidentIO/v1/custom_types.json | 37 ++ .../v1/dataStreams/alertProperties.json | 29 ++ plugins/IncidentIO/v1/dataStreams/alerts.json | 33 ++ .../v1/dataStreams/currentUser.json | 15 + .../v1/dataStreams/incidentAlerts.json | 32 ++ .../v1/dataStreams/incidentEscalations.json | 35 ++ .../v1/dataStreams/incidentProperties.json | 32 ++ .../v1/dataStreams/incidentUpdates.json | 31 ++ .../IncidentIO/v1/dataStreams/incidents.json | 36 ++ .../v1/dataStreams/scheduleOnCall.json | 26 ++ .../v1/dataStreams/scheduleShifts.json | 32 ++ .../IncidentIO/v1/dataStreams/schedules.json | 30 ++ .../v1/dataStreams/scripts/teams.js | 10 + .../v1/dataStreams/scripts/userSchedules.js | 21 + plugins/IncidentIO/v1/dataStreams/teams.json | 28 ++ .../v1/dataStreams/userSchedules.json | 29 ++ plugins/IncidentIO/v1/dataStreams/users.json | 33 ++ .../defaultContent/alertPerspective.dash.json | 50 +++ .../incidentPerspective.dash.json | 178 ++++++++ .../v1/defaultContent/manifest.json | 10 + .../v1/defaultContent/overview.dash.json | 401 ++++++++++++++++++ .../schedulePerspective.dash.json | 101 +++++ .../IncidentIO/v1/defaultContent/scopes.json | 62 +++ .../defaultContent/teamPerspective.dash.json | 46 ++ .../defaultContent/userPerspective.dash.json | 90 ++++ plugins/IncidentIO/v1/docs/README.md | 60 +++ plugins/IncidentIO/v1/icon.svg | 6 + .../v1/indexDefinitions/default.json | 84 ++++ plugins/IncidentIO/v1/metadata.json | 36 ++ plugins/IncidentIO/v1/ui.json | 10 + 32 files changed, 1635 insertions(+) create mode 100644 plugins/IncidentIO/v1/configValidation.json create mode 100644 plugins/IncidentIO/v1/custom_types.json create mode 100644 plugins/IncidentIO/v1/dataStreams/alertProperties.json create mode 100644 plugins/IncidentIO/v1/dataStreams/alerts.json create mode 100644 plugins/IncidentIO/v1/dataStreams/currentUser.json create mode 100644 plugins/IncidentIO/v1/dataStreams/incidentAlerts.json create mode 100644 plugins/IncidentIO/v1/dataStreams/incidentEscalations.json create mode 100644 plugins/IncidentIO/v1/dataStreams/incidentProperties.json create mode 100644 plugins/IncidentIO/v1/dataStreams/incidentUpdates.json create mode 100644 plugins/IncidentIO/v1/dataStreams/incidents.json create mode 100644 plugins/IncidentIO/v1/dataStreams/scheduleOnCall.json create mode 100644 plugins/IncidentIO/v1/dataStreams/scheduleShifts.json create mode 100644 plugins/IncidentIO/v1/dataStreams/schedules.json create mode 100644 plugins/IncidentIO/v1/dataStreams/scripts/teams.js create mode 100644 plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js create mode 100644 plugins/IncidentIO/v1/dataStreams/teams.json create mode 100644 plugins/IncidentIO/v1/dataStreams/userSchedules.json create mode 100644 plugins/IncidentIO/v1/dataStreams/users.json create mode 100644 plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json create mode 100644 plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json create mode 100644 plugins/IncidentIO/v1/defaultContent/manifest.json create mode 100644 plugins/IncidentIO/v1/defaultContent/overview.dash.json create mode 100644 plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json create mode 100644 plugins/IncidentIO/v1/defaultContent/scopes.json create mode 100644 plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json create mode 100644 plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json create mode 100644 plugins/IncidentIO/v1/docs/README.md create mode 100644 plugins/IncidentIO/v1/icon.svg create mode 100644 plugins/IncidentIO/v1/indexDefinitions/default.json create mode 100644 plugins/IncidentIO/v1/metadata.json create mode 100644 plugins/IncidentIO/v1/ui.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c44719cf..6e243e41 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,6 +11,7 @@ plugins/AutoTask/* @TimWheeler-SQUP plugins/DattoRMM/* @TimWheeler-SQUP plugins/DigiCert/* @shaswot77 plugins/Huntress/* @Deenk +plugins/IncidentIO/* @noorulkhan-n plugins/FantasyPremierLeague/* @TimWheeler-SQUP plugins/GoogleSheets/* @kieranlangton plugins/MetOffice/* @blackgrouse diff --git a/plugins/IncidentIO/v1/configValidation.json b/plugins/IncidentIO/v1/configValidation.json new file mode 100644 index 00000000..a3536b99 --- /dev/null +++ b/plugins/IncidentIO/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "currentUser" }, + "required": true, + "error": "Could not authenticate. Check your API key is valid and has not expired.", + "success": "Connected successfully." + } + ] +} diff --git a/plugins/IncidentIO/v1/custom_types.json b/plugins/IncidentIO/v1/custom_types.json new file mode 100644 index 00000000..5d2d226e --- /dev/null +++ b/plugins/IncidentIO/v1/custom_types.json @@ -0,0 +1,37 @@ +[ + { + "name": "Incident", + "sourceType": "Incident", + "icon": "triangle-exclamation", + "singular": "Incident", + "plural": "Incidents" + }, + { + "name": "Alert", + "sourceType": "Alert", + "icon": "bell", + "singular": "Alert", + "plural": "Alerts" + }, + { + "name": "Schedule", + "sourceType": "Schedule", + "icon": "calendar-days", + "singular": "Schedule", + "plural": "Schedules" + }, + { + "name": "Team", + "sourceType": "Team", + "icon": "people-group", + "singular": "Team", + "plural": "Teams" + }, + { + "name": "User", + "sourceType": "User", + "icon": "user", + "singular": "User", + "plural": "Users" + } +] diff --git a/plugins/IncidentIO/v1/dataStreams/alertProperties.json b/plugins/IncidentIO/v1/dataStreams/alertProperties.json new file mode 100644 index 00000000..7eee1312 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/alertProperties.json @@ -0,0 +1,29 @@ +{ + "name": "alertProperties", + "displayName": "Alert Properties", + "description": "Current-state properties for an alert", + "tags": ["Alerts"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/alerts/{{object.rawId}}", + "expandInnerObjects": true, + "pathToData": "alert" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Alert"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, + { "name": "status", "displayName": "Status", "shape": "string" }, + { "name": "description", "displayName": "Description", "shape": "string" }, + { "name": "alert_source_id", "displayName": "Alert Source ID", "shape": "string" }, + { "name": "deduplication_key", "displayName": "Deduplication Key", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "name": "resolved_at", "displayName": "Resolved At", "shape": "date" }, + { "name": "source_url", "displayName": "Source URL", "shape": "url" }, + { "name": "attributes", "displayName": "Attributes", "shape": "json", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/alerts.json b/plugins/IncidentIO/v1/dataStreams/alerts.json new file mode 100644 index 00000000..4e59dc30 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/alerts.json @@ -0,0 +1,33 @@ +{ + "name": "alerts", + "displayName": "Alerts", + "description": "All alerts ingested by alert sources, one row per alert", + "tags": ["Alerts"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v2/alerts", + "pathToData": "alerts", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, + { "name": "status", "displayName": "Status", "shape": "string" }, + { "name": "alert_source_id", "displayName": "Alert Source ID", "shape": "string" }, + { "name": "deduplication_key", "displayName": "Deduplication Key", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "name": "resolved_at", "displayName": "Resolved At", "shape": "date" }, + { "name": "source_url", "displayName": "Source URL", "shape": "url" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/currentUser.json b/plugins/IncidentIO/v1/dataStreams/currentUser.json new file mode 100644 index 00000000..e18f177d --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/currentUser.json @@ -0,0 +1,15 @@ +{ + "name": "currentUser", + "displayName": "Current User", + "description": "Identity and role details for the configured API key", + "tags": ["Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v1/identity" + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [{ "pattern": ".*" }], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json b/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json new file mode 100644 index 00000000..bee7037b --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json @@ -0,0 +1,32 @@ +{ + "name": "incidentAlerts", + "displayName": "Incident Alerts", + "description": "Alerts attached to an incident, one row per incident-alert connection", + "tags": ["Incidents", "Alerts"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/incident_alerts", + "getArgs": [{ "key": "incident_id", "value": "{{object.rawId}}" }], + "expandInnerObjects": true, + "pathToData": "incident_alerts", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "alert.title", "displayName": "Alert Title", "shape": "string", "role": "label" }, + { "name": "alert.status", "displayName": "Alert Status", "shape": "string" }, + { "name": "alert.id", "displayName": "Alert ID", "shape": "string", "visible": false }, + { "name": "alert.created_at", "displayName": "Alert Created At", "shape": "date" }, + { "name": "alert.deduplication_key", "displayName": "Deduplication Key", "shape": "string" }, + { "name": "alert_route_id", "displayName": "Alert Route ID", "shape": "string" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json b/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json new file mode 100644 index 00000000..58e75145 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json @@ -0,0 +1,35 @@ +{ + "name": "incidentEscalations", + "displayName": "Incident Escalations", + "description": "Escalation timeline for an incident, one row per escalation", + "tags": ["Incidents", "On-Call"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/escalations", + "getArgs": [ + { "key": "incident[one_of]", "value": "{{object.rawId}}" }, + { "key": "created_at[date_range]", "value": "{{timeframe.start}}~{{timeframe.end}}" } + ], + "expandInnerObjects": true, + "pathToData": "escalations", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, + { "name": "status", "displayName": "Status", "shape": "string" }, + { "name": "escalation_path_id", "displayName": "Escalation Path ID", "shape": "string" }, + { "name": "priority.name", "displayName": "Priority", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date", "role": "timestamp" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "pattern": ".*" } + ], + "timeframes": true +} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentProperties.json b/plugins/IncidentIO/v1/dataStreams/incidentProperties.json new file mode 100644 index 00000000..dcc63814 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/incidentProperties.json @@ -0,0 +1,32 @@ +{ + "name": "incidentProperties", + "displayName": "Incident Properties", + "description": "Current-state properties for an incident", + "tags": ["Incidents"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/incidents/{{object.rawId}}", + "expandInnerObjects": true, + "pathToData": "incident" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "reference", "displayName": "Reference", "shape": "string" }, + { "name": "mode", "displayName": "Mode", "shape": "string" }, + { "name": "visibility", "displayName": "Visibility", "shape": "string" }, + { "name": "incident_status.name", "displayName": "Status", "shape": "string" }, + { "name": "severity.name", "displayName": "Severity", "shape": "string" }, + { "name": "incident_type.name", "displayName": "Type", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "name": "last_activity_at", "displayName": "Last Activity At", "shape": "date" }, + { "name": "permalink", "displayName": "Permalink", "shape": "url" }, + { "name": "slack_channel_url", "displayName": "Slack Channel URL", "shape": "url" }, + { "name": "has_debrief", "displayName": "Has Debrief", "shape": "boolean" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json b/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json new file mode 100644 index 00000000..8c6b74e4 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json @@ -0,0 +1,31 @@ +{ + "name": "incidentUpdates", + "displayName": "Incident Updates", + "description": "Status and severity change history for an incident, one row per update", + "tags": ["Incidents"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/incident_updates", + "getArgs": [{ "key": "incident_id", "value": "{{object.rawId}}" }], + "expandInnerObjects": true, + "pathToData": "incident_updates", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "created_at", "displayName": "Created At", "shape": "date", "role": "timestamp" }, + { "name": "message", "displayName": "Message", "shape": "string" }, + { "name": "new_incident_status.name", "displayName": "New Status", "shape": "string" }, + { "name": "new_severity.name", "displayName": "New Severity", "shape": "string" }, + { "name": "merged_into_incident_id", "displayName": "Merged Into Incident ID", "shape": "string" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/incidents.json b/plugins/IncidentIO/v1/dataStreams/incidents.json new file mode 100644 index 00000000..8f56e2d4 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/incidents.json @@ -0,0 +1,36 @@ +{ + "name": "incidents", + "displayName": "Incidents", + "description": "All incidents in the organization, one row per incident", + "tags": ["Incidents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v2/incidents", + "expandInnerObjects": true, + "pathToData": "incidents", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "reference", "displayName": "Reference", "shape": "string" }, + { "name": "mode", "displayName": "Mode", "shape": "string" }, + { "name": "visibility", "displayName": "Visibility", "shape": "string" }, + { "name": "incident_status.name", "displayName": "Status", "shape": "string" }, + { "name": "severity.name", "displayName": "Severity", "shape": "string" }, + { "name": "incident_type.name", "displayName": "Type", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "name": "permalink", "displayName": "Permalink", "shape": "url" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/scheduleOnCall.json b/plugins/IncidentIO/v1/dataStreams/scheduleOnCall.json new file mode 100644 index 00000000..982a61d3 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/scheduleOnCall.json @@ -0,0 +1,26 @@ +{ + "name": "scheduleOnCall", + "displayName": "Schedule On-Call", + "description": "Who is currently on-call for a schedule, one row per active shift/layer", + "tags": ["On-Call", "Schedules"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/schedules/{{object.rawId}}", + "pathToData": "schedule.current_shifts", + "expandInnerObjects": true + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Schedule"] } }, + "metadata": [ + { "name": "user.name", "displayName": "On-Call User", "shape": "string", "role": "label" }, + { "name": "user.email", "displayName": "Email", "shape": "string" }, + { "name": "start_at", "displayName": "Start At", "shape": "date", "role": "timestamp" }, + { "name": "end_at", "displayName": "End At", "shape": "date" }, + { "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" }, + { "name": "layer_id", "displayName": "Layer ID", "shape": "string" }, + { "name": "entry_id", "displayName": "Entry ID", "shape": "string", "visible": false }, + { "name": "fingerprint", "displayName": "Fingerprint", "shape": "string", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/scheduleShifts.json b/plugins/IncidentIO/v1/dataStreams/scheduleShifts.json new file mode 100644 index 00000000..46f39e84 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/scheduleShifts.json @@ -0,0 +1,32 @@ +{ + "name": "scheduleShifts", + "displayName": "Schedule Shifts", + "description": "The schedule's effective shifts across a time window, accounting for overrides", + "tags": ["On-Call", "Schedules"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/schedule_entries", + "getArgs": [ + { "key": "schedule_id", "value": "{{object.rawId}}" }, + { "key": "entry_window_start", "value": "{{timeframe.start}}" }, + { "key": "entry_window_end", "value": "{{timeframe.end}}" } + ], + "expandInnerObjects": true, + "pathToData": "schedule_entries.final", + "paging": { "mode": "none" } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Schedule"] } }, + "metadata": [ + { "name": "user.name", "displayName": "On-Call User", "shape": "string", "role": "label" }, + { "name": "user.email", "displayName": "Email", "shape": "string" }, + { "name": "start_at", "displayName": "Start", "shape": "date", "role": "timestamp" }, + { "name": "end_at", "displayName": "End", "shape": "date" }, + { "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" }, + { "name": "layer_id", "displayName": "Layer ID", "shape": "string" }, + { "name": "entry_id", "displayName": "Entry ID", "shape": "string", "visible": false }, + { "name": "fingerprint", "displayName": "Fingerprint", "shape": "string", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": true +} diff --git a/plugins/IncidentIO/v1/dataStreams/schedules.json b/plugins/IncidentIO/v1/dataStreams/schedules.json new file mode 100644 index 00000000..4f80875d --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/schedules.json @@ -0,0 +1,30 @@ +{ + "name": "schedules", + "displayName": "Schedules", + "description": "All on-call schedules in the organization, one row per schedule", + "tags": ["On-Call", "Schedules"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v2/schedules", + "pathToData": "schedules", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "timezone", "displayName": "Timezone", "shape": "string" }, + { "name": "created_at", "displayName": "Created At", "shape": "date" }, + { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, + { "name": "permalink", "displayName": "Permalink", "shape": "url" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/scripts/teams.js b/plugins/IncidentIO/v1/dataStreams/scripts/teams.js new file mode 100644 index 00000000..b917b8aa --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/scripts/teams.js @@ -0,0 +1,10 @@ +// dataStreams/scripts/teams.js +// The v3/teams response nests a `members` array per team. A metadata +// valueExpression cannot compute a length from a sibling array/object +// column (cross-column references receive an already-shaped placeholder, +// not the real array), so the member count is derived here instead, +// against the untouched raw response. +result = (data.teams || []).map((team) => ({ + ...team, + memberCount: (team.members || []).length, +})); diff --git a/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js b/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js new file mode 100644 index 00000000..b2f46ea2 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js @@ -0,0 +1,21 @@ +// dataStreams/scripts/userSchedules.js +// Cross-references the schedules list against the scoped user's id: for each +// schedule, filters current_shifts to the ones where the shift's on-call +// user matches, and emits one row per matching (schedule, shift) pair. This +// can't be expressed with pathToData/valueExpression alone because it joins +// a nested per-schedule array against the scope object's id. +const userId = context.objects[0].rawId; + +result = (data.schedules || []).flatMap((schedule) => + (schedule.current_shifts || []) + .filter((shift) => shift.user && shift.user.id === userId) + .map((shift) => ({ + scheduleId: schedule.id, + scheduleName: schedule.name, + schedulePermalink: schedule.permalink, + start_at: shift.start_at, + end_at: shift.end_at, + rotation_id: shift.rotation_id || null, + layer_id: shift.layer_id || null, + })), +); diff --git a/plugins/IncidentIO/v1/dataStreams/teams.json b/plugins/IncidentIO/v1/dataStreams/teams.json new file mode 100644 index 00000000..75533e10 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/teams.json @@ -0,0 +1,28 @@ +{ + "name": "teams", + "displayName": "Teams", + "description": "All teams in the organization, one row per team", + "tags": ["Teams"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v3/teams", + "postRequestScript": "teams.js", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "members", "displayName": "Members", "shape": "json", "visible": false }, + { "name": "memberCount", "displayName": "Member Count", "shape": "number" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/userSchedules.json b/plugins/IncidentIO/v1/dataStreams/userSchedules.json new file mode 100644 index 00000000..11171172 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/userSchedules.json @@ -0,0 +1,29 @@ +{ + "name": "userSchedules", + "displayName": "User Schedules", + "description": "Schedules the user currently appears in as on-call, one row per matching shift", + "tags": ["On-Call", "Users"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "v2/schedules", + "postRequestScript": "userSchedules.js", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["User"] } }, + "metadata": [ + { "name": "scheduleId", "displayName": "Schedule ID", "shape": "string", "visible": false }, + { "name": "scheduleName", "displayName": "Schedule", "shape": "string", "role": "label" }, + { "name": "schedulePermalink", "displayName": "Schedule Link", "shape": "url" }, + { "name": "start_at", "displayName": "Start", "shape": "date", "role": "timestamp" }, + { "name": "end_at", "displayName": "End", "shape": "date" }, + { "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" }, + { "name": "layer_id", "displayName": "Layer ID", "shape": "string" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/dataStreams/users.json b/plugins/IncidentIO/v1/dataStreams/users.json new file mode 100644 index 00000000..9fd56ba2 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/users.json @@ -0,0 +1,33 @@ +{ + "name": "users", + "displayName": "Users", + "description": "All users in the organization, one row per user", + "tags": ["Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "v2/users", + "expandInnerObjects": true, + "pathToData": "users", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "in": { "realm": "payload", "path": "pagination_meta.after" }, + "out": { "realm": "queryArg", "path": "after" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "email", "displayName": "Email", "shape": "string" }, + { "name": "role", "displayName": "Role", "shape": "string" }, + { "name": "is_active", "displayName": "Active", "shape": "boolean" }, + { "name": "slack_user_id", "displayName": "Slack User ID", "shape": "string" }, + { "name": "seats.on_call", "displayName": "On-Call Seat", "shape": "string" }, + { "name": "seats.response", "displayName": "Response Seat", "shape": "string" }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json new file mode 100644 index 00000000..31f0bd49 --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json @@ -0,0 +1,50 @@ +{ + "name": "Alert Perspective", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": ["{{variables.[Alert]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "b50960f0-7ad7-4fee-90fd-81350aa67e0a", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[alertProperties]}}", + "name": "alertProperties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Alerts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Alert]}}" + }, + "variables": ["{{variables.[Alert]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "hiddenColumns": ["attributes"] + } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json new file mode 100644 index 00000000..b806d48e --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json @@ -0,0 +1,178 @@ +{ + "name": "Incident Perspective", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": ["{{variables.[Incident]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "d52e805f-b8a5-40f1-abf7-ce1fb8132a1a", + "x": 0, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidentProperties]}}", + "name": "incidentProperties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Incidents]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Incident]}}" + }, + "variables": ["{{variables.[Incident]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { "transpose": true } + } + } + } + }, + { + "i": "6723cfcc-12f5-4248-9033-b73ecd74ed70", + "x": 2, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Update Timeline", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidentUpdates]}}", + "name": "incidentUpdates", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["created_at", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Incidents]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Incident]}}" + }, + "variables": ["{{variables.[Incident]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["created_at", "new_incident_status.name", "new_severity.name", "message"], + "hiddenColumns": ["id", "merged_into_incident_id"], + "columnDisplayNames": { + "created_at": "Time", + "new_incident_status.name": "New Status", + "new_severity.name": "New Severity" + } + } + } + } + } + }, + { + "i": "eae3558e-f48c-453e-a6d9-c689e85e2af8", + "x": 0, + "y": 4, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Attached Alerts", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidentAlerts]}}", + "name": "incidentAlerts", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["alert.created_at", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Incidents]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Incident]}}" + }, + "variables": ["{{variables.[Incident]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["alert.title", "alert.status", "alert.created_at"], + "hiddenColumns": ["id", "alert.id", "alert.deduplication_key", "alert_route_id"], + "columnDisplayNames": { + "alert.title": "Alert", + "alert.status": "Status", + "alert.created_at": "Created At" + } + } + } + } + } + }, + { + "i": "f0919907-4ab2-4f88-b31e-b06a45044f36", + "x": 2, + "y": 4, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Escalations", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentEscalations]}}", + "name": "incidentEscalations", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["created_at", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Incidents]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Incident]}}" + }, + "variables": ["{{variables.[Incident]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["title", "status", "priority.name", "created_at", "updated_at"], + "hiddenColumns": ["id", "escalation_path_id"], + "columnDisplayNames": { + "priority.name": "Priority", + "created_at": "Created At", + "updated_at": "Updated At" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/defaultContent/manifest.json b/plugins/IncidentIO/v1/defaultContent/manifest.json new file mode 100644 index 00000000..b17d6a7c --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/manifest.json @@ -0,0 +1,10 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "incidentPerspective", "type": "dashboard" }, + { "name": "alertPerspective", "type": "dashboard" }, + { "name": "schedulePerspective", "type": "dashboard" }, + { "name": "teamPerspective", "type": "dashboard" }, + { "name": "userPerspective", "type": "dashboard" } + ] +} diff --git a/plugins/IncidentIO/v1/defaultContent/overview.dash.json b/plugins/IncidentIO/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..3516da00 --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/overview.dash.json @@ -0,0 +1,401 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last24hours", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "4a85c50c-12a8-4088-aaad-0314febc841c", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Incidents]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Incidents" + } + } + } + } + }, + { + "i": "1e9416d4-423e-4ef3-a25e-b420000e4f22", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Alerts", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Alerts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Alerts" + } + } + } + } + }, + { + "i": "56555b98-b97a-4474-bf36-49e157235e70", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Schedules", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Schedules]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Schedules" + } + } + } + } + }, + { + "i": "8ae18f2d-080f-4f8d-adf3-37ce50cee107", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Teams", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Teams]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Teams" + } + } + } + } + }, + { + "i": "20dbbbe4-d77a-464e-840c-c7d2d7af97be", + "x": 0, + "y": 2, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incidents by Status", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["incident_status.name", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "incident_status.name_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "ce9049e7-1621-464e-bc4d-b95b5254fbbf", + "x": 2, + "y": 2, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incidents by Severity", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["severity.name", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "severity.name_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "88851ce4-a8e9-49b6-be33-dde1f8fd66b6", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Incidents by Type", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["incident_type.name", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + }, + "sort": { "by": [["count", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "incident_type.name_uniqueValues", + "yAxisData": ["count"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": true, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + }, + { + "i": "cc30e099-0702-4b7e-95c4-21f3ba5074cc", + "x": 2, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Schedules", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[schedules]}}", + "name": "schedules", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "timezone", "permalink"], + "hiddenColumns": ["id", "created_at", "updated_at"] + } + } + } + } + }, + { + "i": "e83e942a-8eeb-41f6-846b-5933eb12b4b9", + "x": 0, + "y": 8, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["created_at", "desc"]], "top": 15 } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "incident_status.name", + "severity.name", + "incident_type.name", + "created_at", + "permalink" + ], + "hiddenColumns": ["id", "reference", "mode", "visibility", "updated_at"], + "columnDisplayNames": { + "incident_status.name": "Status", + "severity.name": "Severity", + "incident_type.name": "Type", + "created_at": "Created At" + } + } + } + } + } + }, + { + "i": "249f6989-d623-4686-b84f-ff81290e5f4b", + "x": 0, + "y": 11, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Alerts", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[alerts]}}", + "name": "alerts", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["created_at", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["title", "status", "created_at", "resolved_at", "source_url"], + "hiddenColumns": ["id", "alert_source_id", "deduplication_key", "updated_at"], + "columnDisplayNames": { + "created_at": "Created At", + "resolved_at": "Resolved At", + "source_url": "Source URL" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json new file mode 100644 index 00000000..885b1318 --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json @@ -0,0 +1,101 @@ +{ + "name": "Schedule Perspective", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": ["{{variables.[Schedule]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "2f589ed0-e52e-4a53-9445-10ba46af5272", + "x": 0, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Current On-Call", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[scheduleOnCall]}}", + "name": "scheduleOnCall", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Schedules]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Schedule]}}" + }, + "variables": ["{{variables.[Schedule]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user.name", "user.email", "start_at", "end_at"], + "hiddenColumns": ["rotation_id", "layer_id", "entry_id", "fingerprint"], + "columnDisplayNames": { + "user.name": "On-Call User", + "user.email": "Email", + "start_at": "Start", + "end_at": "End" + } + } + } + } + } + }, + { + "i": "eb35ef1f-62d5-42b4-9b21-3a211155f5d8", + "x": 2, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Shifts", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[scheduleShifts]}}", + "name": "scheduleShifts", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["start_at", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Schedules]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Schedule]}}" + }, + "variables": ["{{variables.[Schedule]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user.name", "user.email", "start_at", "end_at"], + "hiddenColumns": ["rotation_id", "layer_id", "entry_id", "fingerprint"], + "columnDisplayNames": { + "user.name": "On-Call User", + "user.email": "Email", + "start_at": "Start", + "end_at": "End" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/defaultContent/scopes.json b/plugins/IncidentIO/v1/defaultContent/scopes.json new file mode 100644 index 00000000..ee1f342c --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/scopes.json @@ -0,0 +1,62 @@ +[ + { + "name": "Incidents", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Incident"] } + }, + "variable": { + "name": "Incident", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Alerts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Alert"] } + }, + "variable": { + "name": "Alert", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Schedules", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Schedule"] } + }, + "variable": { + "name": "Schedule", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Teams", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Team"] } + }, + "variable": { + "name": "Team", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Users", + "matches": { + "sourceType": { "type": "oneOf", "values": ["User"] } + }, + "variable": { + "name": "User", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json new file mode 100644 index 00000000..ef19f7f0 --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json @@ -0,0 +1,46 @@ +{ + "name": "Team Perspective", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": ["{{variables.[Team]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "3554c41a-1268-4ac9-bf7b-8472314134c8", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Teams]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Team]}}" + }, + "variables": ["{{variables.[Team]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { "transpose": true } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json new file mode 100644 index 00000000..85ec8f7e --- /dev/null +++ b/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json @@ -0,0 +1,90 @@ +{ + "name": "User Perspective", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": ["{{variables.[User]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "6deb863b-e900-4f8a-8064-04ddc6e96f12", + "x": 0, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Users]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[User]}}" + }, + "variables": ["{{variables.[User]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { "transpose": true } + } + } + } + }, + { + "i": "5665353e-5263-4a1d-91d5-d275bb167341", + "x": 2, + "y": 0, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Schedules", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[userSchedules]}}", + "name": "userSchedules", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Users]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[User]}}" + }, + "variables": ["{{variables.[User]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["scheduleName", "start_at", "end_at", "schedulePermalink"], + "hiddenColumns": ["rotation_id", "layer_id"], + "columnDisplayNames": { + "scheduleName": "Schedule", + "start_at": "Start", + "end_at": "End", + "schedulePermalink": "Link" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/IncidentIO/v1/docs/README.md b/plugins/IncidentIO/v1/docs/README.md new file mode 100644 index 00000000..ed7e5cbf --- /dev/null +++ b/plugins/IncidentIO/v1/docs/README.md @@ -0,0 +1,60 @@ +Monitor your [incident.io](https://incident.io) incidents, alerts, on-call schedules, teams and users in SquaredUp, via the [incident.io API](https://docs.incident.io/api-reference/introduction). + +## Setup + +You will need an incident.io **API key**. + +1. Sign in to your [incident.io dashboard](https://app.incident.io). +2. Go to **Settings → API keys** and click **Create API key**. Give it at least the **View data** permission — the plugin never creates, updates, or deletes anything in incident.io. +3. Copy the generated key and paste it into the **API key** field below. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ----------------- | -------- | +| **API key** | Authenticates every request via the `Authorization: Bearer` header. | incident.io dashboard → **Settings → API keys**. | Yes | + +On save, the plugin validates the key by calling the identity endpoint; an invalid or expired key fails setup with an authentication error. + +## What this plugin monitors + +- **Incidents** — status, severity, type, timeline of updates, attached alerts, and escalations for each incident. +- **Alerts** — alerts ingested by your alert sources, and which incidents they're attached to. +- **On-call schedules** — who's on call right now, upcoming/recent shifts, and which schedules a given user belongs to. +- **Teams and users** — team membership counts, and each user's role and on-call/response seat access. + +The out-of-the-box dashboards include an estate-wide **Overview** plus a perspective for each of **Incident**, **Alert**, **Schedule**, **Team**, and **User**. + +## Data streams + +- **Current User** — identity and role for the configured API key (used to validate setup; hidden from the tile editor). +- **Incident Properties** — current status, severity, type and timestamps for one incident. +- **Incident Updates** — the history of status and severity changes for one incident. +- **Incident Alerts** — the alerts attached to one incident. +- **Incident Escalations** — the escalation timeline (who was paged, when acked/resolved) for one incident, over a timeframe. +- **Alert Properties** — current status, description and timestamps for one alert. +- **Schedule On-Call** — the current on-call shift(s) for one schedule. +- **Schedule Shifts** — the effective shift schedule for one schedule, over a timeframe. +- **User Schedules** — the on-call schedules a given user currently appears in. + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Incident** | `GET /v2/incidents` | An incident declared in incident.io. | +| **Alert** | `GET /v2/alerts` | An alert ingested from a third-party alert source. | +| **Schedule** | `GET /v2/schedules` | An on-call rotation schedule. | +| **Team** | `GET /v3/teams` | A team, with its member count. | +| **User** | `GET /v2/users` | A person in your incident.io organization. | + +**Relationships:** Incidents, Alerts and Schedules are cross-referenced from their respective perspective dashboards (attached alerts and escalations for an incident, on-call shifts for a schedule and a user), but are not modelled as direct graph links. + +## Known limitations + +- **Some list endpoints cap page size lower than others** — the alerts, incident-alerts and escalations endpoints accept at most 50 records per page (vs 100–250+ elsewhere); large volumes of alert/escalation history are paged automatically but may take longer to import or query. +- **Alert and escalation data depends on your incident.io setup** — if no alert sources or on-call escalation paths are configured, the Alert object type, and the Incident Alerts / Incident Escalations / Alert Properties streams, will simply be empty rather than erroring. +- **Schedule shifts show recent history, not future shifts** — the Schedule perspective's shift tile uses the standard dashboard timeframe (which only looks backward), so upcoming on-call shifts aren't shown; check the incident.io on-call UI for future rotations. +- **Teams requires the `catalog_entries.view` scope** on your API key — without it, team import and the Team object type will be unavailable. +- **User Schedules checks only the first page of schedules** — for organizations with more than ~100 schedules, a user's on-call status on schedules beyond the first page won't be reflected. +- **Rate limits** — the incident.io API allows up to 1,200 requests/minute per key, with a lower 60/minute limit specifically on listing incidents; very large estates may import more slowly as a result. +- **Read-only** — the plugin never creates, modifies, or deletes anything in incident.io. diff --git a/plugins/IncidentIO/v1/icon.svg b/plugins/IncidentIO/v1/icon.svg new file mode 100644 index 00000000..e43a9526 --- /dev/null +++ b/plugins/IncidentIO/v1/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/IncidentIO/v1/indexDefinitions/default.json b/plugins/IncidentIO/v1/indexDefinitions/default.json new file mode 100644 index 00000000..661bf093 --- /dev/null +++ b/plugins/IncidentIO/v1/indexDefinitions/default.json @@ -0,0 +1,84 @@ +{ + "steps": [ + { + "name": "incidents", + "dataStream": { "name": "incidents" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Incident" }, + "properties": [ + "reference", + "mode", + "visibility", + { "status": "incident_status.name" }, + { "severity": "severity.name" }, + { "incidentType": "incident_type.name" }, + "created_at", + "updated_at", + "permalink" + ] + } + }, + { + "name": "alerts", + "dataStream": { "name": "alerts" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "title", + "type": { "value": "Alert" }, + "properties": [ + "status", + "alert_source_id", + "deduplication_key", + "created_at", + "updated_at", + "resolved_at", + "source_url" + ] + } + }, + { + "name": "schedules", + "dataStream": { "name": "schedules" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Schedule" }, + "properties": ["timezone", "created_at", "updated_at", "permalink"] + } + }, + { + "name": "teams", + "dataStream": { "name": "teams" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Team" }, + "properties": ["memberCount"] + } + }, + { + "name": "users", + "dataStream": { "name": "users" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "User" }, + "properties": [ + "email", + "role", + "is_active", + "slack_user_id", + { "onCallSeat": "seats.on_call" }, + { "responseSeat": "seats.response" } + ] + } + } + ] +} diff --git a/plugins/IncidentIO/v1/metadata.json b/plugins/IncidentIO/v1/metadata.json new file mode 100644 index 00000000..1158bb45 --- /dev/null +++ b/plugins/IncidentIO/v1/metadata.json @@ -0,0 +1,36 @@ +{ + "name": "incident-io", + "displayName": "incident.io", + "version": "1.0.0", + "author": { "name": "noorul-khan", "type": "community" }, + "description": "Monitor incidents, alerts, on-call schedules, teams and users from incident.io in SquaredUp.", + "category": "Alert Management", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": ["incident management", "on-call", "alerting", "incident response"], + "objectTypes": ["Incident", "Alert", "Schedule", "Team", "User"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/IncidentIO/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/IncidentIO/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "https://api.incident.io/", + "authMode": "none", + "headers": [{ "key": "Authorization", "value": "Bearer {{apiKey}}" }], + "queryArgs": [] + } + } +} diff --git a/plugins/IncidentIO/v1/ui.json b/plugins/IncidentIO/v1/ui.json new file mode 100644 index 00000000..a8b552ca --- /dev/null +++ b/plugins/IncidentIO/v1/ui.json @@ -0,0 +1,10 @@ +[ + { + "type": "password", + "name": "apiKey", + "label": "API key", + "help": "Create an API key in incident.io under **Settings → API keys** with at least the **View data** permission.", + "placeholder": "inc_xxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "validation": { "required": true } + } +] From 5943a91a7302e1faa62854bb500d5acb74deff35 Mon Sep 17 00:00:00 2001 From: Noorul Huda <135027955+noorulkhan-n@users.noreply.github.com> Date: Thu, 3 Sep 2026 12:34:59 +0530 Subject: [PATCH 2/8] Modify CODEOWNERS for IncidentIO path Updated CODEOWNERS to change path for IncidentIO. --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6e243e41..d689d1fc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,7 @@ plugins/AutoTask/* @TimWheeler-SQUP plugins/DattoRMM/* @TimWheeler-SQUP plugins/DigiCert/* @shaswot77 plugins/Huntress/* @Deenk -plugins/IncidentIO/* @noorulkhan-n +plugins/IncidentIO/ @noorulkhan-n plugins/FantasyPremierLeague/* @TimWheeler-SQUP plugins/GoogleSheets/* @kieranlangton plugins/MetOffice/* @blackgrouse From 39d9333529e7a00d8ff1080b4bc882c9da51a9ac Mon Sep 17 00:00:00 2001 From: Noorul Huda <135027955+noorulkhan-n@users.noreply.github.com> Date: Thu, 3 Sep 2026 12:39:23 +0530 Subject: [PATCH 3/8] Add objectLimit to currentUser configuration --- plugins/IncidentIO/v1/dataStreams/currentUser.json | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/IncidentIO/v1/dataStreams/currentUser.json b/plugins/IncidentIO/v1/dataStreams/currentUser.json index e18f177d..60e5cb89 100644 --- a/plugins/IncidentIO/v1/dataStreams/currentUser.json +++ b/plugins/IncidentIO/v1/dataStreams/currentUser.json @@ -5,6 +5,7 @@ "tags": ["Users"], "baseDataSourceName": "httpRequestUnscoped", "config": { + "objectLimit":1, "httpMethod": "get", "endpointPath": "v1/identity" }, From 9ed85224eccc2ed8e7d889125761017fa58e5922 Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Thu, 3 Sep 2026 12:52:14 +0530 Subject: [PATCH 4/8] Document why userSchedules.js already aggregates across all schedule pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit flagged the script's per-page `data.schedules` access as only checking the first page. Verified empirically (forced page_size=1 against the live API) that the platform re-invokes postRequestScript once per page as pagination advances, concatenating every invocation's result into the final output — matching the same pattern used by Rootly/Kubernetes/NinjaOne streams elsewhere in this repo. No functional change; added a comment recording the proof so the pattern isn't mistaken for a bug again. Co-Authored-By: Claude Sonnet 5 --- .../IncidentIO/v1/dataStreams/scripts/userSchedules.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js b/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js index b2f46ea2..305405d3 100644 --- a/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js +++ b/plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js @@ -6,6 +6,15 @@ // a nested per-schedule array against the scope object's id. const userId = context.objects[0].rawId; +// This script only ever sees ONE page of `schedules` per invocation (`data` is that +// page's raw response body) — but the platform re-invokes it once per page as the +// `paging` config advances the `after` cursor, and concatenates every invocation's +// `result` into the stream's final output. Filtering per page and letting the +// platform concatenate is equivalent to concatenating all pages before filtering, +// since this is a stateless per-row filter with no cross-page aggregation — so every +// schedule, on every page, is checked. Confirmed empirically against this endpoint by +// forcing page_size=1 (2 pages for 2 real schedules) and seeing both schedules' +// shifts in the final result, each tagged with its own page's cursor. result = (data.schedules || []).flatMap((schedule) => (schedule.current_shifts || []) .filter((shift) => shift.user && shift.user.id === userId) From 284b4c6ae2acd21efd9954a280fdd8f1ca37658a Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Thu, 3 Sep 2026 16:57:08 +0530 Subject: [PATCH 5/8] Remove invalid objectLimit from currentUser.json objectLimit is a top-level stream property (not a config key), and it has no effect on an unscoped stream (matches: "none") like currentUser since there's no object scope to limit. Caused squaredup validate to fail with "Unrecognized key: objectLimit". Co-Authored-By: Claude Sonnet 5 --- plugins/IncidentIO/v1/dataStreams/currentUser.json | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/IncidentIO/v1/dataStreams/currentUser.json b/plugins/IncidentIO/v1/dataStreams/currentUser.json index 60e5cb89..e18f177d 100644 --- a/plugins/IncidentIO/v1/dataStreams/currentUser.json +++ b/plugins/IncidentIO/v1/dataStreams/currentUser.json @@ -5,7 +5,6 @@ "tags": ["Users"], "baseDataSourceName": "httpRequestUnscoped", "config": { - "objectLimit":1, "httpMethod": "get", "endpointPath": "v1/identity" }, From 80182d1472d350dec6a570c3df2f30e21b431204 Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Thu, 3 Sep 2026 20:05:17 +0530 Subject: [PATCH 6/8] Drop Incident/Alert indexing; rename dashboards; rebuild Overview Addresses reviewer feedback on PR #128 (Dave Clarke): - Rename all perspective dashboards to drop the redundant "Perspective" suffix ("Incident", "Alert", "User", ... is enough on their own). - Stop indexing Incident and Alert as graph objects. They're created and resolved far faster than the platform's default ~12h reindex interval, so a new incident could be invisible as an object for hours - the same reasoning AutoTask/TeamDynamix already apply to Tickets in this repo (never indexed, served live via data streams instead). Removed the incidents/alerts import steps, the Incident/Alert object types and custom_types entries, and confirmed via a live re-index that the previously-imported objects are cleanly dropped from the graph. - Removed the Incident/Alert perspective dashboards and their five per-object scoped streams (incidentProperties, incidentUpdates, incidentAlerts, incidentEscalations, alertProperties), since per-object drilldown requires an indexed object to scope a dashboard variable by. Un-hid the incidents/alerts streams (previously import-only) so they serve as the primary live data source instead, matching AutoTask's Tickets Overview pattern. - Reworked the Overview dashboard: Total Incidents/Alerts KPIs now count from the live streams instead of the (now nonexistent) indexed objects, and added Open Incidents / Firing Alerts KPI tiles. - Updated docs/README.md to reflect the new architecture and explain why Incidents/Alerts aren't indexed. Co-Authored-By: Claude Sonnet 5 --- plugins/IncidentIO/v1/custom_types.json | 14 -- .../v1/dataStreams/alertProperties.json | 29 --- plugins/IncidentIO/v1/dataStreams/alerts.json | 1 - .../v1/dataStreams/incidentAlerts.json | 32 ---- .../v1/dataStreams/incidentEscalations.json | 35 ---- .../v1/dataStreams/incidentProperties.json | 32 ---- .../v1/dataStreams/incidentUpdates.json | 31 --- .../IncidentIO/v1/dataStreams/incidents.json | 2 +- .../defaultContent/alertPerspective.dash.json | 50 ----- .../incidentPerspective.dash.json | 178 ------------------ .../v1/defaultContent/manifest.json | 2 - .../v1/defaultContent/overview.dash.json | 109 +++++++++-- .../schedulePerspective.dash.json | 2 +- .../IncidentIO/v1/defaultContent/scopes.json | 24 --- .../defaultContent/teamPerspective.dash.json | 2 +- .../defaultContent/userPerspective.dash.json | 2 +- plugins/IncidentIO/v1/docs/README.md | 26 ++- .../v1/indexDefinitions/default.json | 40 ---- plugins/IncidentIO/v1/metadata.json | 2 +- 19 files changed, 107 insertions(+), 506 deletions(-) delete mode 100644 plugins/IncidentIO/v1/dataStreams/alertProperties.json delete mode 100644 plugins/IncidentIO/v1/dataStreams/incidentAlerts.json delete mode 100644 plugins/IncidentIO/v1/dataStreams/incidentEscalations.json delete mode 100644 plugins/IncidentIO/v1/dataStreams/incidentProperties.json delete mode 100644 plugins/IncidentIO/v1/dataStreams/incidentUpdates.json delete mode 100644 plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json delete mode 100644 plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json diff --git a/plugins/IncidentIO/v1/custom_types.json b/plugins/IncidentIO/v1/custom_types.json index 5d2d226e..e87d1a38 100644 --- a/plugins/IncidentIO/v1/custom_types.json +++ b/plugins/IncidentIO/v1/custom_types.json @@ -1,18 +1,4 @@ [ - { - "name": "Incident", - "sourceType": "Incident", - "icon": "triangle-exclamation", - "singular": "Incident", - "plural": "Incidents" - }, - { - "name": "Alert", - "sourceType": "Alert", - "icon": "bell", - "singular": "Alert", - "plural": "Alerts" - }, { "name": "Schedule", "sourceType": "Schedule", diff --git a/plugins/IncidentIO/v1/dataStreams/alertProperties.json b/plugins/IncidentIO/v1/dataStreams/alertProperties.json deleted file mode 100644 index 7eee1312..00000000 --- a/plugins/IncidentIO/v1/dataStreams/alertProperties.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "alertProperties", - "displayName": "Alert Properties", - "description": "Current-state properties for an alert", - "tags": ["Alerts"], - "baseDataSourceName": "httpRequestScopedSingle", - "config": { - "httpMethod": "get", - "endpointPath": "v2/alerts/{{object.rawId}}", - "expandInnerObjects": true, - "pathToData": "alert" - }, - "matches": { "sourceType": { "type": "oneOf", "values": ["Alert"] } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, - { "name": "status", "displayName": "Status", "shape": "string" }, - { "name": "description", "displayName": "Description", "shape": "string" }, - { "name": "alert_source_id", "displayName": "Alert Source ID", "shape": "string" }, - { "name": "deduplication_key", "displayName": "Deduplication Key", "shape": "string" }, - { "name": "created_at", "displayName": "Created At", "shape": "date" }, - { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, - { "name": "resolved_at", "displayName": "Resolved At", "shape": "date" }, - { "name": "source_url", "displayName": "Source URL", "shape": "url" }, - { "name": "attributes", "displayName": "Attributes", "shape": "json", "visible": false }, - { "pattern": ".*" } - ], - "timeframes": false -} diff --git a/plugins/IncidentIO/v1/dataStreams/alerts.json b/plugins/IncidentIO/v1/dataStreams/alerts.json index 4e59dc30..dd70891e 100644 --- a/plugins/IncidentIO/v1/dataStreams/alerts.json +++ b/plugins/IncidentIO/v1/dataStreams/alerts.json @@ -16,7 +16,6 @@ } }, "matches": "none", - "visibility": { "type": "hidden" }, "metadata": [ { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, diff --git a/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json b/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json deleted file mode 100644 index bee7037b..00000000 --- a/plugins/IncidentIO/v1/dataStreams/incidentAlerts.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "incidentAlerts", - "displayName": "Incident Alerts", - "description": "Alerts attached to an incident, one row per incident-alert connection", - "tags": ["Incidents", "Alerts"], - "baseDataSourceName": "httpRequestScopedSingle", - "config": { - "httpMethod": "get", - "endpointPath": "v2/incident_alerts", - "getArgs": [{ "key": "incident_id", "value": "{{object.rawId}}" }], - "expandInnerObjects": true, - "pathToData": "incident_alerts", - "paging": { - "mode": "token", - "pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" }, - "in": { "realm": "payload", "path": "pagination_meta.after" }, - "out": { "realm": "queryArg", "path": "after" } - } - }, - "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "alert.title", "displayName": "Alert Title", "shape": "string", "role": "label" }, - { "name": "alert.status", "displayName": "Alert Status", "shape": "string" }, - { "name": "alert.id", "displayName": "Alert ID", "shape": "string", "visible": false }, - { "name": "alert.created_at", "displayName": "Alert Created At", "shape": "date" }, - { "name": "alert.deduplication_key", "displayName": "Deduplication Key", "shape": "string" }, - { "name": "alert_route_id", "displayName": "Alert Route ID", "shape": "string" }, - { "pattern": ".*" } - ], - "timeframes": false -} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json b/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json deleted file mode 100644 index 58e75145..00000000 --- a/plugins/IncidentIO/v1/dataStreams/incidentEscalations.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "incidentEscalations", - "displayName": "Incident Escalations", - "description": "Escalation timeline for an incident, one row per escalation", - "tags": ["Incidents", "On-Call"], - "baseDataSourceName": "httpRequestScopedSingle", - "config": { - "httpMethod": "get", - "endpointPath": "v2/escalations", - "getArgs": [ - { "key": "incident[one_of]", "value": "{{object.rawId}}" }, - { "key": "created_at[date_range]", "value": "{{timeframe.start}}~{{timeframe.end}}" } - ], - "expandInnerObjects": true, - "pathToData": "escalations", - "paging": { - "mode": "token", - "pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" }, - "in": { "realm": "payload", "path": "pagination_meta.after" }, - "out": { "realm": "queryArg", "path": "after" } - } - }, - "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, - { "name": "status", "displayName": "Status", "shape": "string" }, - { "name": "escalation_path_id", "displayName": "Escalation Path ID", "shape": "string" }, - { "name": "priority.name", "displayName": "Priority", "shape": "string" }, - { "name": "created_at", "displayName": "Created At", "shape": "date", "role": "timestamp" }, - { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, - { "pattern": ".*" } - ], - "timeframes": true -} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentProperties.json b/plugins/IncidentIO/v1/dataStreams/incidentProperties.json deleted file mode 100644 index dcc63814..00000000 --- a/plugins/IncidentIO/v1/dataStreams/incidentProperties.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "incidentProperties", - "displayName": "Incident Properties", - "description": "Current-state properties for an incident", - "tags": ["Incidents"], - "baseDataSourceName": "httpRequestScopedSingle", - "config": { - "httpMethod": "get", - "endpointPath": "v2/incidents/{{object.rawId}}", - "expandInnerObjects": true, - "pathToData": "incident" - }, - "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, - { "name": "reference", "displayName": "Reference", "shape": "string" }, - { "name": "mode", "displayName": "Mode", "shape": "string" }, - { "name": "visibility", "displayName": "Visibility", "shape": "string" }, - { "name": "incident_status.name", "displayName": "Status", "shape": "string" }, - { "name": "severity.name", "displayName": "Severity", "shape": "string" }, - { "name": "incident_type.name", "displayName": "Type", "shape": "string" }, - { "name": "created_at", "displayName": "Created At", "shape": "date" }, - { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, - { "name": "last_activity_at", "displayName": "Last Activity At", "shape": "date" }, - { "name": "permalink", "displayName": "Permalink", "shape": "url" }, - { "name": "slack_channel_url", "displayName": "Slack Channel URL", "shape": "url" }, - { "name": "has_debrief", "displayName": "Has Debrief", "shape": "boolean" }, - { "pattern": ".*" } - ], - "timeframes": false -} diff --git a/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json b/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json deleted file mode 100644 index 8c6b74e4..00000000 --- a/plugins/IncidentIO/v1/dataStreams/incidentUpdates.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "incidentUpdates", - "displayName": "Incident Updates", - "description": "Status and severity change history for an incident, one row per update", - "tags": ["Incidents"], - "baseDataSourceName": "httpRequestScopedSingle", - "config": { - "httpMethod": "get", - "endpointPath": "v2/incident_updates", - "getArgs": [{ "key": "incident_id", "value": "{{object.rawId}}" }], - "expandInnerObjects": true, - "pathToData": "incident_updates", - "paging": { - "mode": "token", - "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, - "in": { "realm": "payload", "path": "pagination_meta.after" }, - "out": { "realm": "queryArg", "path": "after" } - } - }, - "matches": { "sourceType": { "type": "oneOf", "values": ["Incident"] } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "created_at", "displayName": "Created At", "shape": "date", "role": "timestamp" }, - { "name": "message", "displayName": "Message", "shape": "string" }, - { "name": "new_incident_status.name", "displayName": "New Status", "shape": "string" }, - { "name": "new_severity.name", "displayName": "New Severity", "shape": "string" }, - { "name": "merged_into_incident_id", "displayName": "Merged Into Incident ID", "shape": "string" }, - { "pattern": ".*" } - ], - "timeframes": false -} diff --git a/plugins/IncidentIO/v1/dataStreams/incidents.json b/plugins/IncidentIO/v1/dataStreams/incidents.json index 8f56e2d4..a9d432ec 100644 --- a/plugins/IncidentIO/v1/dataStreams/incidents.json +++ b/plugins/IncidentIO/v1/dataStreams/incidents.json @@ -17,7 +17,6 @@ } }, "matches": "none", - "visibility": { "type": "hidden" }, "metadata": [ { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, @@ -25,6 +24,7 @@ { "name": "mode", "displayName": "Mode", "shape": "string" }, { "name": "visibility", "displayName": "Visibility", "shape": "string" }, { "name": "incident_status.name", "displayName": "Status", "shape": "string" }, + { "name": "incident_status.category", "displayName": "Status Category", "shape": "string" }, { "name": "severity.name", "displayName": "Severity", "shape": "string" }, { "name": "incident_type.name", "displayName": "Type", "shape": "string" }, { "name": "created_at", "displayName": "Created At", "shape": "date" }, diff --git a/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json deleted file mode 100644 index 31f0bd49..00000000 --- a/plugins/IncidentIO/v1/defaultContent/alertPerspective.dash.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "Alert Perspective", - "schemaVersion": "1.5", - "timeframe": "last24hours", - "variables": ["{{variables.[Alert]}}"], - "dashboard": { - "_type": "layout/grid", - "columns": 4, - "version": 1, - "contents": [ - { - "i": "b50960f0-7ad7-4fee-90fd-81350aa67e0a", - "x": 0, - "y": 0, - "w": 4, - "h": 4, - "moved": false, - "static": false, - "z": 0, - "config": { - "_type": "tile/data-stream", - "title": "Properties", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "timeframe": "none", - "dataStream": { - "id": "{{dataStreams.[alertProperties]}}", - "name": "alertProperties", - "pluginConfigId": "{{configId}}" - }, - "scope": { - "scope": "{{scopes.[Alerts]}}", - "workspace": "{{workspaceId}}", - "variable": "{{variables.[Alert]}}" - }, - "variables": ["{{variables.[Alert]}}"], - "visualisation": { - "type": "data-stream-table", - "config": { - "data-stream-table": { - "transpose": true, - "hiddenColumns": ["attributes"] - } - } - } - } - } - ] - } -} diff --git a/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json deleted file mode 100644 index b806d48e..00000000 --- a/plugins/IncidentIO/v1/defaultContent/incidentPerspective.dash.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "name": "Incident Perspective", - "schemaVersion": "1.5", - "timeframe": "last30days", - "variables": ["{{variables.[Incident]}}"], - "dashboard": { - "_type": "layout/grid", - "columns": 4, - "version": 1, - "contents": [ - { - "i": "d52e805f-b8a5-40f1-abf7-ce1fb8132a1a", - "x": 0, - "y": 0, - "w": 2, - "h": 4, - "moved": false, - "static": false, - "z": 0, - "config": { - "_type": "tile/data-stream", - "title": "Properties", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "timeframe": "none", - "dataStream": { - "id": "{{dataStreams.[incidentProperties]}}", - "name": "incidentProperties", - "pluginConfigId": "{{configId}}" - }, - "scope": { - "scope": "{{scopes.[Incidents]}}", - "workspace": "{{workspaceId}}", - "variable": "{{variables.[Incident]}}" - }, - "variables": ["{{variables.[Incident]}}"], - "visualisation": { - "type": "data-stream-table", - "config": { - "data-stream-table": { "transpose": true } - } - } - } - }, - { - "i": "6723cfcc-12f5-4248-9033-b73ecd74ed70", - "x": 2, - "y": 0, - "w": 2, - "h": 4, - "moved": false, - "static": false, - "z": 0, - "config": { - "_type": "tile/data-stream", - "title": "Update Timeline", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "timeframe": "none", - "dataStream": { - "id": "{{dataStreams.[incidentUpdates]}}", - "name": "incidentUpdates", - "pluginConfigId": "{{configId}}", - "sort": { "by": [["created_at", "desc"]] } - }, - "scope": { - "scope": "{{scopes.[Incidents]}}", - "workspace": "{{workspaceId}}", - "variable": "{{variables.[Incident]}}" - }, - "variables": ["{{variables.[Incident]}}"], - "visualisation": { - "type": "data-stream-table", - "config": { - "data-stream-table": { - "transpose": false, - "columnOrder": ["created_at", "new_incident_status.name", "new_severity.name", "message"], - "hiddenColumns": ["id", "merged_into_incident_id"], - "columnDisplayNames": { - "created_at": "Time", - "new_incident_status.name": "New Status", - "new_severity.name": "New Severity" - } - } - } - } - } - }, - { - "i": "eae3558e-f48c-453e-a6d9-c689e85e2af8", - "x": 0, - "y": 4, - "w": 2, - "h": 4, - "moved": false, - "static": false, - "z": 0, - "config": { - "_type": "tile/data-stream", - "title": "Attached Alerts", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "timeframe": "none", - "dataStream": { - "id": "{{dataStreams.[incidentAlerts]}}", - "name": "incidentAlerts", - "pluginConfigId": "{{configId}}", - "sort": { "by": [["alert.created_at", "desc"]] } - }, - "scope": { - "scope": "{{scopes.[Incidents]}}", - "workspace": "{{workspaceId}}", - "variable": "{{variables.[Incident]}}" - }, - "variables": ["{{variables.[Incident]}}"], - "visualisation": { - "type": "data-stream-table", - "config": { - "data-stream-table": { - "transpose": false, - "columnOrder": ["alert.title", "alert.status", "alert.created_at"], - "hiddenColumns": ["id", "alert.id", "alert.deduplication_key", "alert_route_id"], - "columnDisplayNames": { - "alert.title": "Alert", - "alert.status": "Status", - "alert.created_at": "Created At" - } - } - } - } - } - }, - { - "i": "f0919907-4ab2-4f88-b31e-b06a45044f36", - "x": 2, - "y": 4, - "w": 2, - "h": 4, - "moved": false, - "static": false, - "z": 0, - "config": { - "_type": "tile/data-stream", - "title": "Escalations", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "dataStream": { - "id": "{{dataStreams.[incidentEscalations]}}", - "name": "incidentEscalations", - "pluginConfigId": "{{configId}}", - "sort": { "by": [["created_at", "desc"]] } - }, - "scope": { - "scope": "{{scopes.[Incidents]}}", - "workspace": "{{workspaceId}}", - "variable": "{{variables.[Incident]}}" - }, - "variables": ["{{variables.[Incident]}}"], - "visualisation": { - "type": "data-stream-table", - "config": { - "data-stream-table": { - "transpose": false, - "columnOrder": ["title", "status", "priority.name", "created_at", "updated_at"], - "hiddenColumns": ["id", "escalation_path_id"], - "columnDisplayNames": { - "priority.name": "Priority", - "created_at": "Created At", - "updated_at": "Updated At" - } - } - } - } - } - } - ] - } -} diff --git a/plugins/IncidentIO/v1/defaultContent/manifest.json b/plugins/IncidentIO/v1/defaultContent/manifest.json index b17d6a7c..e99f07c5 100644 --- a/plugins/IncidentIO/v1/defaultContent/manifest.json +++ b/plugins/IncidentIO/v1/defaultContent/manifest.json @@ -1,8 +1,6 @@ { "items": [ { "name": "overview", "type": "dashboard" }, - { "name": "incidentPerspective", "type": "dashboard" }, - { "name": "alertPerspective", "type": "dashboard" }, { "name": "schedulePerspective", "type": "dashboard" }, { "name": "teamPerspective", "type": "dashboard" }, { "name": "userPerspective", "type": "dashboard" } diff --git a/plugins/IncidentIO/v1/defaultContent/overview.dash.json b/plugins/IncidentIO/v1/defaultContent/overview.dash.json index 3516da00..bc378eb8 100644 --- a/plugins/IncidentIO/v1/defaultContent/overview.dash.json +++ b/plugins/IncidentIO/v1/defaultContent/overview.dash.json @@ -24,14 +24,11 @@ "activePluginConfigIds": ["{{configId}}"], "timeframe": "none", "dataStream": { - "id": "datastream-properties", - "name": "properties", + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", "group": { "by": [], "aggregate": [{ "type": "count" }] } }, - "scope": { - "scope": "{{scopes.[Incidents]}}", - "workspace": "{{workspaceId}}" - }, "visualisation": { "type": "data-stream-scalar", "config": { @@ -60,14 +57,11 @@ "activePluginConfigIds": ["{{configId}}"], "timeframe": "none", "dataStream": { - "id": "datastream-properties", - "name": "properties", + "id": "{{dataStreams.[alerts]}}", + "name": "alerts", + "pluginConfigId": "{{configId}}", "group": { "by": [], "aggregate": [{ "type": "count" }] } }, - "scope": { - "scope": "{{scopes.[Alerts]}}", - "workspace": "{{workspaceId}}" - }, "visualisation": { "type": "data-stream-scalar", "config": { @@ -153,10 +147,89 @@ } }, { - "i": "20dbbbe4-d77a-464e-840c-c7d2d7af97be", + "i": "3b6c1a2d-9e4f-4a1b-8c3d-5f7e2a9b4c6e", "x": 0, "y": 2, "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Open Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [ + { "column": "incident_status.category", "operation": "notequals", "value": "closed" }, + { "column": "incident_status.category", "operation": "notequals", "value": "declined" }, + { "column": "incident_status.category", "operation": "notequals", "value": "canceled" }, + { "column": "incident_status.category", "operation": "notequals", "value": "merged" } + ] + }, + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Open Incidents" + } + } + } + } + }, + { + "i": "7d2e8f1c-4b5a-4e9d-b6c8-1a3f5d7e9b2c", + "x": 2, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Firing Alerts", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[alerts]}}", + "name": "alerts", + "pluginConfigId": "{{configId}}", + "filter": { + "multiOperation": "and", + "filters": [{ "column": "status", "operation": "equals", "value": "firing" }] + }, + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Firing Alerts" + } + } + } + } + }, + { + "i": "20dbbbe4-d77a-464e-840c-c7d2d7af97be", + "x": 0, + "y": 4, + "w": 2, "h": 3, "moved": false, "static": false, @@ -194,7 +267,7 @@ { "i": "ce9049e7-1621-464e-bc4d-b95b5254fbbf", "x": 2, - "y": 2, + "y": 4, "w": 2, "h": 3, "moved": false, @@ -233,7 +306,7 @@ { "i": "88851ce4-a8e9-49b6-be33-dde1f8fd66b6", "x": 0, - "y": 5, + "y": 7, "w": 2, "h": 3, "moved": false, @@ -283,7 +356,7 @@ { "i": "cc30e099-0702-4b7e-95c4-21f3ba5074cc", "x": 2, - "y": 5, + "y": 7, "w": 2, "h": 3, "moved": false, @@ -315,7 +388,7 @@ { "i": "e83e942a-8eeb-41f6-846b-5933eb12b4b9", "x": 0, - "y": 8, + "y": 10, "w": 4, "h": 3, "moved": false, @@ -361,7 +434,7 @@ { "i": "249f6989-d623-4686-b84f-ff81290e5f4b", "x": 0, - "y": 11, + "y": 13, "w": 4, "h": 3, "moved": false, diff --git a/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json index 885b1318..814da992 100644 --- a/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json +++ b/plugins/IncidentIO/v1/defaultContent/schedulePerspective.dash.json @@ -1,5 +1,5 @@ { - "name": "Schedule Perspective", + "name": "Schedule", "schemaVersion": "1.5", "timeframe": "last7days", "variables": ["{{variables.[Schedule]}}"], diff --git a/plugins/IncidentIO/v1/defaultContent/scopes.json b/plugins/IncidentIO/v1/defaultContent/scopes.json index ee1f342c..e7ca3f78 100644 --- a/plugins/IncidentIO/v1/defaultContent/scopes.json +++ b/plugins/IncidentIO/v1/defaultContent/scopes.json @@ -1,28 +1,4 @@ [ - { - "name": "Incidents", - "matches": { - "sourceType": { "type": "oneOf", "values": ["Incident"] } - }, - "variable": { - "name": "Incident", - "allowMultipleSelection": false, - "default": "none", - "type": "object" - } - }, - { - "name": "Alerts", - "matches": { - "sourceType": { "type": "oneOf", "values": ["Alert"] } - }, - "variable": { - "name": "Alert", - "allowMultipleSelection": false, - "default": "none", - "type": "object" - } - }, { "name": "Schedules", "matches": { diff --git a/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json index ef19f7f0..48f28a5e 100644 --- a/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json +++ b/plugins/IncidentIO/v1/defaultContent/teamPerspective.dash.json @@ -1,5 +1,5 @@ { - "name": "Team Perspective", + "name": "Team", "schemaVersion": "1.5", "timeframe": "none", "variables": ["{{variables.[Team]}}"], diff --git a/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json b/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json index 85ec8f7e..f7545298 100644 --- a/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json +++ b/plugins/IncidentIO/v1/defaultContent/userPerspective.dash.json @@ -1,5 +1,5 @@ { - "name": "User Perspective", + "name": "User", "schemaVersion": "1.5", "timeframe": "none", "variables": ["{{variables.[User]}}"], diff --git a/plugins/IncidentIO/v1/docs/README.md b/plugins/IncidentIO/v1/docs/README.md index ed7e5cbf..ab9d83b6 100644 --- a/plugins/IncidentIO/v1/docs/README.md +++ b/plugins/IncidentIO/v1/docs/README.md @@ -18,21 +18,18 @@ On save, the plugin validates the key by calling the identity endpoint; an inval ## What this plugin monitors -- **Incidents** — status, severity, type, timeline of updates, attached alerts, and escalations for each incident. -- **Alerts** — alerts ingested by your alert sources, and which incidents they're attached to. -- **On-call schedules** — who's on call right now, upcoming/recent shifts, and which schedules a given user belongs to. +- **Incidents** — live status, severity and type breakdowns, open/total counts, and a recent-incidents table. +- **Alerts** — live status and a recent-alerts table, including how many are currently firing. +- **On-call schedules** — who's on call right now, recent shifts, and which schedules a given user belongs to. - **Teams and users** — team membership counts, and each user's role and on-call/response seat access. -The out-of-the-box dashboards include an estate-wide **Overview** plus a perspective for each of **Incident**, **Alert**, **Schedule**, **Team**, and **User**. +The out-of-the-box dashboards include an estate-wide **Overview** plus a perspective for each of **Schedule**, **Team**, and **User**. ## Data streams - **Current User** — identity and role for the configured API key (used to validate setup; hidden from the tile editor). -- **Incident Properties** — current status, severity, type and timestamps for one incident. -- **Incident Updates** — the history of status and severity changes for one incident. -- **Incident Alerts** — the alerts attached to one incident. -- **Incident Escalations** — the escalation timeline (who was paged, when acked/resolved) for one incident, over a timeframe. -- **Alert Properties** — current status, description and timestamps for one alert. +- **Incidents** — all incidents in the organization, one row per incident. +- **Alerts** — all alerts ingested by your alert sources, one row per alert. - **Schedule On-Call** — the current on-call shift(s) for one schedule. - **Schedule Shifts** — the effective shift schedule for one schedule, over a timeframe. - **User Schedules** — the on-call schedules a given user currently appears in. @@ -41,20 +38,19 @@ The out-of-the-box dashboards include an estate-wide **Overview** plus a perspec | Object type | API source | Represents | | ----------- | ---------- | ---------- | -| **Incident** | `GET /v2/incidents` | An incident declared in incident.io. | -| **Alert** | `GET /v2/alerts` | An alert ingested from a third-party alert source. | | **Schedule** | `GET /v2/schedules` | An on-call rotation schedule. | | **Team** | `GET /v3/teams` | A team, with its member count. | | **User** | `GET /v2/users` | A person in your incident.io organization. | -**Relationships:** Incidents, Alerts and Schedules are cross-referenced from their respective perspective dashboards (attached alerts and escalations for an incident, on-call shifts for a schedule and a user), but are not modelled as direct graph links. +**Incidents and Alerts are not indexed as objects.** They're created and resolved far faster than the platform's default ~12-hour reindex interval — indexing them would mean a new incident could be invisible for hours. Instead, they're served live via the **Incidents** and **Alerts** data streams (always fresh, no reindex lag) and shown as filterable/groupable tiles on the Overview dashboard, rather than as individual drilldown pages. ## Known limitations -- **Some list endpoints cap page size lower than others** — the alerts, incident-alerts and escalations endpoints accept at most 50 records per page (vs 100–250+ elsewhere); large volumes of alert/escalation history are paged automatically but may take longer to import or query. -- **Alert and escalation data depends on your incident.io setup** — if no alert sources or on-call escalation paths are configured, the Alert object type, and the Incident Alerts / Incident Escalations / Alert Properties streams, will simply be empty rather than erroring. +- **Alerts cap page size at 50 records** (vs 100+ for schedules/teams/users); large alert volumes are paged automatically but may take longer to query. +- **Alert data depends on your incident.io setup** — if no alert sources are configured, the Alerts stream and its Overview tiles will simply be empty rather than erroring. +- **No per-incident or per-alert drilldown dashboard** — since neither is indexed as an object (see above), there's no dedicated page for a single incident's update timeline, attached alerts, or escalations; use the incident.io web app for that level of detail. - **Schedule shifts show recent history, not future shifts** — the Schedule perspective's shift tile uses the standard dashboard timeframe (which only looks backward), so upcoming on-call shifts aren't shown; check the incident.io on-call UI for future rotations. - **Teams requires the `catalog_entries.view` scope** on your API key — without it, team import and the Team object type will be unavailable. - **User Schedules checks only the first page of schedules** — for organizations with more than ~100 schedules, a user's on-call status on schedules beyond the first page won't be reflected. -- **Rate limits** — the incident.io API allows up to 1,200 requests/minute per key, with a lower 60/minute limit specifically on listing incidents; very large estates may import more slowly as a result. +- **Rate limits** — the incident.io API allows up to 1,200 requests/minute per key, with a lower 60/minute limit specifically on listing incidents; very large estates may query more slowly as a result. - **Read-only** — the plugin never creates, modifies, or deletes anything in incident.io. diff --git a/plugins/IncidentIO/v1/indexDefinitions/default.json b/plugins/IncidentIO/v1/indexDefinitions/default.json index 661bf093..9830bab2 100644 --- a/plugins/IncidentIO/v1/indexDefinitions/default.json +++ b/plugins/IncidentIO/v1/indexDefinitions/default.json @@ -1,45 +1,5 @@ { "steps": [ - { - "name": "incidents", - "dataStream": { "name": "incidents" }, - "timeframe": "none", - "objectMapping": { - "id": "id", - "name": "name", - "type": { "value": "Incident" }, - "properties": [ - "reference", - "mode", - "visibility", - { "status": "incident_status.name" }, - { "severity": "severity.name" }, - { "incidentType": "incident_type.name" }, - "created_at", - "updated_at", - "permalink" - ] - } - }, - { - "name": "alerts", - "dataStream": { "name": "alerts" }, - "timeframe": "none", - "objectMapping": { - "id": "id", - "name": "title", - "type": { "value": "Alert" }, - "properties": [ - "status", - "alert_source_id", - "deduplication_key", - "created_at", - "updated_at", - "resolved_at", - "source_url" - ] - } - }, { "name": "schedules", "dataStream": { "name": "schedules" }, diff --git a/plugins/IncidentIO/v1/metadata.json b/plugins/IncidentIO/v1/metadata.json index 1158bb45..7d806ceb 100644 --- a/plugins/IncidentIO/v1/metadata.json +++ b/plugins/IncidentIO/v1/metadata.json @@ -10,7 +10,7 @@ "importNotSupported": false, "restrictedToPlatforms": [], "keywords": ["incident management", "on-call", "alerting", "incident response"], - "objectTypes": ["Incident", "Alert", "Schedule", "Team", "User"], + "objectTypes": ["Schedule", "Team", "User"], "links": [ { "category": "documentation", From bdca9940f6dc7123eefaf4e8ae8a338e82dc43bc Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Fri, 4 Sep 2026 08:30:43 +0530 Subject: [PATCH 7/8] Unpack schedule rotation membership instead of showing raw objects/arrays The schedules stream's catch-all metadata pattern surfaced team_ids and config as unshaped raw values, rendering as "[object Array]"/"[object Object]" in the Schedules table on the Overview dashboard. Added a postRequestScript to flatten config.rotations[].users (two levels deep, not reachable via pathToData/valueExpression) into a readable, deduplicated "Members" column plus a rotation count, and dropped the raw team_ids/config passthrough. Verified against the live tenant and confirmed the Schedule object import (same underlying stream) still writes correctly. Co-Authored-By: Claude Sonnet 5 --- .../IncidentIO/v1/dataStreams/schedules.json | 8 ++++--- .../v1/dataStreams/scripts/schedules.js | 23 +++++++++++++++++++ .../v1/defaultContent/overview.dash.json | 4 ++-- 3 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 plugins/IncidentIO/v1/dataStreams/scripts/schedules.js diff --git a/plugins/IncidentIO/v1/dataStreams/schedules.json b/plugins/IncidentIO/v1/dataStreams/schedules.json index 4f80875d..ba2db41f 100644 --- a/plugins/IncidentIO/v1/dataStreams/schedules.json +++ b/plugins/IncidentIO/v1/dataStreams/schedules.json @@ -7,7 +7,7 @@ "config": { "httpMethod": "get", "endpointPath": "v2/schedules", - "pathToData": "schedules", + "postRequestScript": "schedules.js", "paging": { "mode": "token", "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, @@ -21,10 +21,12 @@ { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, { "name": "timezone", "displayName": "Timezone", "shape": "string" }, + { "name": "rotationCount", "displayName": "Rotations", "shape": "number" }, + { "name": "members", "displayName": "Members", "shape": "string" }, + { "name": "memberCount", "displayName": "Member Count", "shape": "number", "visible": false }, { "name": "created_at", "displayName": "Created At", "shape": "date" }, { "name": "updated_at", "displayName": "Updated At", "shape": "date" }, - { "name": "permalink", "displayName": "Permalink", "shape": "url" }, - { "pattern": ".*" } + { "name": "permalink", "displayName": "Permalink", "shape": "url" } ], "timeframes": false } diff --git a/plugins/IncidentIO/v1/dataStreams/scripts/schedules.js b/plugins/IncidentIO/v1/dataStreams/scripts/schedules.js new file mode 100644 index 00000000..5bb0bda0 --- /dev/null +++ b/plugins/IncidentIO/v1/dataStreams/scripts/schedules.js @@ -0,0 +1,23 @@ +// dataStreams/scripts/schedules.js +// The raw response nests rotation membership two levels deep +// (schedule.config.rotations[].users[]), which pathToData/valueExpression can't +// reach - flatten it into a readable, deduplicated list of member names. +result = (data.schedules || []).map((schedule) => { + const rotations = (schedule.config && schedule.config.rotations) || []; + const members = new Map(); + rotations.forEach((rotation) => + (rotation.users || []).forEach((user) => members.set(user.id, user.name)), + ); + + return { + id: schedule.id, + name: schedule.name, + timezone: schedule.timezone, + rotationCount: rotations.length, + members: [...members.values()].join(", "), + memberCount: members.size, + created_at: schedule.created_at, + updated_at: schedule.updated_at, + permalink: schedule.permalink, + }; +}); diff --git a/plugins/IncidentIO/v1/defaultContent/overview.dash.json b/plugins/IncidentIO/v1/defaultContent/overview.dash.json index bc378eb8..a039d4d3 100644 --- a/plugins/IncidentIO/v1/defaultContent/overview.dash.json +++ b/plugins/IncidentIO/v1/defaultContent/overview.dash.json @@ -378,8 +378,8 @@ "config": { "data-stream-table": { "transpose": false, - "columnOrder": ["name", "timezone", "permalink"], - "hiddenColumns": ["id", "created_at", "updated_at"] + "columnOrder": ["name", "timezone", "members", "permalink"], + "hiddenColumns": ["id", "rotationCount", "created_at", "updated_at"] } } } From 1b445ac6c5a85702107de13411ad30dcbf621356 Mon Sep 17 00:00:00 2001 From: Noorul Khan Date: Mon, 7 Sep 2026 20:25:11 +0530 Subject: [PATCH 8/8] Add timeframe picker to Incidents data stream Filters on created_at via the API's date_range operator (gte+lte combined returns a 422 "Expected exactly one operator type"). None is the default so existing Overview tiles keep their all-time totals. --- .../IncidentIO/v1/dataStreams/incidents.json | 25 +++++++++++++++++-- plugins/IncidentIO/v1/docs/README.md | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/plugins/IncidentIO/v1/dataStreams/incidents.json b/plugins/IncidentIO/v1/dataStreams/incidents.json index a9d432ec..b42c7973 100644 --- a/plugins/IncidentIO/v1/dataStreams/incidents.json +++ b/plugins/IncidentIO/v1/dataStreams/incidents.json @@ -1,13 +1,19 @@ { "name": "incidents", "displayName": "Incidents", - "description": "All incidents in the organization, one row per incident", + "description": "All incidents in the organization within the selected timeframe, one row per incident", "tags": ["Incidents"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", "endpointPath": "v2/incidents", "expandInnerObjects": true, + "getArgs": [ + { + "key": "created_at[date_range]", + "value": "{{timeframe.enum === 'none' ? null : (timeframe.start + '~' + timeframe.end)}}" + } + ], "pathToData": "incidents", "paging": { "mode": "token", @@ -32,5 +38,20 @@ { "name": "permalink", "displayName": "Permalink", "shape": "url" }, { "pattern": ".*" } ], - "timeframes": false + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days", + "last30days", + "thisMonth", + "thisQuarter", + "thisYear", + "lastMonth", + "lastQuarter", + "lastYear", + "none" + ], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" } diff --git a/plugins/IncidentIO/v1/docs/README.md b/plugins/IncidentIO/v1/docs/README.md index ab9d83b6..b5a9f592 100644 --- a/plugins/IncidentIO/v1/docs/README.md +++ b/plugins/IncidentIO/v1/docs/README.md @@ -28,7 +28,7 @@ The out-of-the-box dashboards include an estate-wide **Overview** plus a perspec ## Data streams - **Current User** — identity and role for the configured API key (used to validate setup; hidden from the tile editor). -- **Incidents** — all incidents in the organization, one row per incident. +- **Incidents** — all incidents in the organization within the selected timeframe, one row per incident. - **Alerts** — all alerts ingested by your alert sources, one row per alert. - **Schedule On-Call** — the current on-call shift(s) for one schedule. - **Schedule Shifts** — the effective shift schedule for one schedule, over a timeframe.