From e66a3611d69c86bd8a5d668e4e51ad7268e787e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 10:41:11 +0000 Subject: [PATCH 1/8] Add PagerDuty low-code plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new SquaredUp LCP plugin for PagerDuty covering incidents (raw and aggregated Analytics v2 metrics), services, users, teams, schedules, escalation policies, on-call schedules and notifications, with an API-token auth flow and Overview/On-Call/Service/User/Team dashboards. Built with the build-plugin skill. Live testing via the squaredup CLI (deploy/test/index) was not possible in this environment — the sandbox's network egress proxy blocks api.pagerduty.com and api.squaredup.com — so the plugin has been validated for JSON/JS syntax and internal consistency only; deploy/auth/import/stream testing against a live tenant is still needed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01RBjQ6TAfpF7XLYJByz8NVb --- plugins/PagerDuty/v1/configValidation.json | 11 + plugins/PagerDuty/v1/custom_types.json | 37 +++ .../v1/dataStreams/escalationPolicies.json | 45 +++ .../v1/dataStreams/incidentMetrics.json | 84 +++++ .../dataStreams/incidentMetricsBreakdown.json | 77 +++++ .../PagerDuty/v1/dataStreams/incidents.json | 118 +++++++ .../v1/dataStreams/notifications.json | 60 ++++ plugins/PagerDuty/v1/dataStreams/onCall.json | 71 +++++ .../v1/dataStreams/responderMetrics.json | 64 ++++ .../PagerDuty/v1/dataStreams/schedules.json | 31 ++ .../v1/dataStreams/scripts/incidents.js | 22 ++ .../v1/dataStreams/scripts/services.js | 22 ++ .../PagerDuty/v1/dataStreams/scripts/users.js | 21 ++ .../PagerDuty/v1/dataStreams/services.json | 59 ++++ plugins/PagerDuty/v1/dataStreams/teams.json | 30 ++ plugins/PagerDuty/v1/dataStreams/users.json | 44 +++ .../v1/dataStreams/validateAuth.json | 17 + .../PagerDuty/v1/defaultContent/manifest.json | 9 + .../v1/defaultContent/onCall.dash.json | 122 +++++++ .../v1/defaultContent/overview.dash.json | 299 ++++++++++++++++++ .../PagerDuty/v1/defaultContent/scopes.json | 32 ++ .../servicePerspective.dash.json | 292 +++++++++++++++++ .../defaultContent/teamPerspective.dash.json | 259 +++++++++++++++ .../defaultContent/userPerspective.dash.json | 285 +++++++++++++++++ plugins/PagerDuty/v1/docs/README.md | 71 +++++ plugins/PagerDuty/v1/icon.svg | 6 + .../v1/indexDefinitions/default.json | 66 ++++ plugins/PagerDuty/v1/metadata.json | 39 +++ plugins/PagerDuty/v1/ui.json | 30 ++ 29 files changed, 2323 insertions(+) create mode 100644 plugins/PagerDuty/v1/configValidation.json create mode 100644 plugins/PagerDuty/v1/custom_types.json create mode 100644 plugins/PagerDuty/v1/dataStreams/escalationPolicies.json create mode 100644 plugins/PagerDuty/v1/dataStreams/incidentMetrics.json create mode 100644 plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json create mode 100644 plugins/PagerDuty/v1/dataStreams/incidents.json create mode 100644 plugins/PagerDuty/v1/dataStreams/notifications.json create mode 100644 plugins/PagerDuty/v1/dataStreams/onCall.json create mode 100644 plugins/PagerDuty/v1/dataStreams/responderMetrics.json create mode 100644 plugins/PagerDuty/v1/dataStreams/schedules.json create mode 100644 plugins/PagerDuty/v1/dataStreams/scripts/incidents.js create mode 100644 plugins/PagerDuty/v1/dataStreams/scripts/services.js create mode 100644 plugins/PagerDuty/v1/dataStreams/scripts/users.js create mode 100644 plugins/PagerDuty/v1/dataStreams/services.json create mode 100644 plugins/PagerDuty/v1/dataStreams/teams.json create mode 100644 plugins/PagerDuty/v1/dataStreams/users.json create mode 100644 plugins/PagerDuty/v1/dataStreams/validateAuth.json create mode 100644 plugins/PagerDuty/v1/defaultContent/manifest.json create mode 100644 plugins/PagerDuty/v1/defaultContent/onCall.dash.json create mode 100644 plugins/PagerDuty/v1/defaultContent/overview.dash.json create mode 100644 plugins/PagerDuty/v1/defaultContent/scopes.json create mode 100644 plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json create mode 100644 plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json create mode 100644 plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json create mode 100644 plugins/PagerDuty/v1/docs/README.md create mode 100644 plugins/PagerDuty/v1/icon.svg create mode 100644 plugins/PagerDuty/v1/indexDefinitions/default.json create mode 100644 plugins/PagerDuty/v1/metadata.json create mode 100644 plugins/PagerDuty/v1/ui.json diff --git a/plugins/PagerDuty/v1/configValidation.json b/plugins/PagerDuty/v1/configValidation.json new file mode 100644 index 00000000..04f305cb --- /dev/null +++ b/plugins/PagerDuty/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "validateAuth" }, + "required": true, + "error": "Could not authenticate with PagerDuty. Check that your API token and service region are correct.", + "success": "Connected to PagerDuty successfully." + } + ] +} diff --git a/plugins/PagerDuty/v1/custom_types.json b/plugins/PagerDuty/v1/custom_types.json new file mode 100644 index 00000000..5609c431 --- /dev/null +++ b/plugins/PagerDuty/v1/custom_types.json @@ -0,0 +1,37 @@ +[ + { + "name": "PagerDuty Service", + "sourceType": "PagerDuty Service", + "icon": "bell", + "singular": "Service", + "plural": "Services" + }, + { + "name": "PagerDuty User", + "sourceType": "PagerDuty User", + "icon": "user", + "singular": "User", + "plural": "Users" + }, + { + "name": "PagerDuty Team", + "sourceType": "PagerDuty Team", + "icon": "people-group", + "singular": "Team", + "plural": "Teams" + }, + { + "name": "PagerDuty Schedule", + "sourceType": "PagerDuty Schedule", + "icon": "calendar-days", + "singular": "Schedule", + "plural": "Schedules" + }, + { + "name": "PagerDuty Escalation Policy", + "sourceType": "PagerDuty Escalation Policy", + "icon": "diagram-project", + "singular": "Escalation Policy", + "plural": "Escalation Policies" + } +] diff --git a/plugins/PagerDuty/v1/dataStreams/escalationPolicies.json b/plugins/PagerDuty/v1/dataStreams/escalationPolicies.json new file mode 100644 index 00000000..fa09164b --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/escalationPolicies.json @@ -0,0 +1,45 @@ +{ + "name": "escalationPolicies", + "displayName": "Escalation Policies (Import)", + "description": "One row per PagerDuty escalation policy with its team associations", + "tags": ["Escalation"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "escalation_policies", + "pathToData": "escalation_policies", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "escalation_policies" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + } + }, + "matches": "none", + "metadata": [ + { "name": "id", "shape": "string" }, + { "name": "name", "shape": "string" }, + { "name": "num_loops", "displayName": "Number of Loops", "shape": "number" }, + { "name": "teams", "visible": false }, + { + "name": "teamIds", + "displayName": "Team IDs", + "computed": true, + "valueExpression": "{{ ($['teams'] || []).map(t => t.id).join(',') }}", + "shape": "string" + }, + { + "name": "teamNames", + "displayName": "Team Names", + "computed": true, + "valueExpression": "{{ ($['teams'] || []).map(t => t.summary).join(', ') }}", + "shape": "string" + } + ], + "timeframes": false, + "visibility": { "type": "hidden" } +} diff --git a/plugins/PagerDuty/v1/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v1/dataStreams/incidentMetrics.json new file mode 100644 index 00000000..a817d6b0 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/incidentMetrics.json @@ -0,0 +1,84 @@ +{ + "name": "incidentMetrics", + "displayName": "Incident Metrics", + "description": "Account-wide aggregated incident analytics — MTTA, MTTR, escalation and interruption counts, optionally bucketed by day, week or month", + "tags": ["Incidents", "Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "analytics/metrics/incidents/all", + "postBody": { + "filters": { + "created_at_start": "{{timeframe.start}}", + "created_at_end": "{{timeframe.end}}", + "urgency": "{{urgency || null}}", + "team_ids": "{{team?.map(o => o.rawId) || null}}", + "service_ids": "{{service?.map(o => o.rawId) || null}}" + }, + "time_zone": "{{dataSource.timezone}}", + "aggregate_unit": "{{aggregateUnit || null}}" + }, + "pathToData": "data" + }, + "matches": "none", + "ui": [ + { + "type": "switch", + "name": "aggregateUnit", + "label": "Bucket by", + "help": "Leave unset for a single account-wide summary row over the selected timeframe.", + "options": [ + { "value": "day", "label": "Day" }, + { "value": "week", "label": "Week" }, + { "value": "month", "label": "Month" } + ] + }, + { + "type": "radio", + "name": "urgency", + "label": "Urgency (optional)", + "options": [ + { "value": "high", "label": "High" }, + { "value": "low", "label": "Low" } + ] + }, + { + "type": "objects", + "name": "service", + "label": "Service (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } + }, + { + "type": "objects", + "name": "team", + "label": "Team (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } + } + ], + "metadata": [ + { "name": "range_start", "displayName": "Period", "shape": "date", "role": "timestamp" }, + { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, + { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, + { "name": "total_incidents_auto_resolved", "displayName": "Auto-Resolved", "shape": "number" }, + { "name": "total_incidents_manual_escalated", "displayName": "Manually Escalated", "shape": "number" }, + { "name": "total_incidents_timeout_escalated", "displayName": "Timeout-Escalated", "shape": "number" }, + { "name": "total_incidents_reassigned", "displayName": "Reassigned", "shape": "number" }, + { "name": "total_escalation_count", "displayName": "Escalations", "shape": "number" }, + { "name": "total_interruptions", "displayName": "Interruptions", "shape": "number" }, + { "name": "total_business_hour_interruptions", "displayName": "Business-Hour Interruptions", "shape": "number", "visible": false }, + { "name": "total_off_hour_interruptions", "displayName": "Off-Hour Interruptions", "shape": "number", "visible": false }, + { "name": "total_sleep_hour_interruptions", "displayName": "Sleep-Hour Interruptions", "shape": "number", "visible": false }, + { "name": "total_notifications", "displayName": "Notifications", "shape": "number", "visible": false }, + { "name": "mean_seconds_to_first_ack", "displayName": "Mean Time to Acknowledge", "shape": "seconds" }, + { "name": "mean_seconds_to_resolve", "displayName": "Mean Time to Resolve", "shape": "seconds" }, + { "name": "mean_seconds_to_engage", "displayName": "Mean Time to Engage", "shape": "seconds", "visible": false }, + { "name": "mean_seconds_to_mobilize", "displayName": "Mean Time to Mobilize", "shape": "seconds", "visible": false }, + { "name": "mean_engaged_seconds", "displayName": "Mean Engaged Time", "shape": "seconds", "visible": false }, + { "name": "total_engaged_seconds", "displayName": "Total Engaged Time", "shape": "seconds", "visible": false }, + { "name": "mean_assignment_count", "displayName": "Mean Assignments", "shape": "number", "visible": false }, + { "name": "mean_engaged_user_count", "displayName": "Mean Engaged Users", "shape": "number", "visible": false }, + { "name": "up_time_pct", "displayName": "Uptime", "shape": "percent" } + ], + "timeframes": true, + "defaultTimeframe": "last7days" +} diff --git a/plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json new file mode 100644 index 00000000..a623f341 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json @@ -0,0 +1,77 @@ +{ + "name": "incidentMetricsBreakdown", + "displayName": "Incident Metrics by Service/Team/Escalation Policy", + "description": "Aggregated incident analytics grouped by service, team or escalation policy, one row per group", + "tags": ["Incidents", "Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "analytics/metrics/incidents/{{groupBy}}", + "postBody": { + "filters": { + "created_at_start": "{{timeframe.start}}", + "created_at_end": "{{timeframe.end}}", + "urgency": "{{urgency || null}}", + "team_ids": "{{team?.map(o => o.rawId) || null}}", + "service_ids": "{{service?.map(o => o.rawId) || null}}" + }, + "time_zone": "{{dataSource.timezone}}" + }, + "pathToData": "data" + }, + "matches": "none", + "ui": [ + { + "type": "switch", + "name": "groupBy", + "label": "Group by", + "defaultValue": "services", + "options": [ + { "value": "services", "label": "Service" }, + { "value": "teams", "label": "Team" }, + { "value": "escalation_policies", "label": "Escalation Policy" } + ], + "validation": { "required": true } + }, + { + "type": "radio", + "name": "urgency", + "label": "Urgency (optional)", + "options": [ + { "value": "high", "label": "High" }, + { "value": "low", "label": "Low" } + ] + }, + { + "type": "objects", + "name": "service", + "label": "Service (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } + }, + { + "type": "objects", + "name": "team", + "label": "Team (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } + } + ], + "metadata": [ + { "name": "service_id", "displayName": "Service ID", "shape": "string", "visible": false }, + { "name": "service_name", "displayName": "Service", "shape": "string", "role": "label" }, + { "name": "team_id", "displayName": "Team ID", "shape": "string", "visible": false }, + { "name": "team_name", "displayName": "Team", "shape": "string" }, + { "name": "escalation_policy_id", "displayName": "Escalation Policy ID", "shape": "string", "visible": false }, + { "name": "escalation_policy_name", "displayName": "Escalation Policy", "shape": "string" }, + { "name": "distinct_responder_count", "displayName": "Distinct Responders", "shape": "number" }, + { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, + { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, + { "name": "total_escalation_count", "displayName": "Escalations", "shape": "number" }, + { "name": "total_interruptions", "displayName": "Interruptions", "shape": "number" }, + { "name": "mean_seconds_to_first_ack", "displayName": "Mean Time to Acknowledge", "shape": "seconds" }, + { "name": "mean_seconds_to_resolve", "displayName": "Mean Time to Resolve", "shape": "seconds" }, + { "name": "up_time_pct", "displayName": "Uptime", "shape": "percent" }, + { "sourceId": "service_id", "sourceType": "PagerDuty Service", "name": "service_name" } + ], + "timeframes": true, + "defaultTimeframe": "last7days" +} diff --git a/plugins/PagerDuty/v1/dataStreams/incidents.json b/plugins/PagerDuty/v1/dataStreams/incidents.json new file mode 100644 index 00000000..69d898ec --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/incidents.json @@ -0,0 +1,118 @@ +{ + "name": "incidents", + "displayName": "Incidents", + "description": "PagerDuty incidents filterable by status, urgency, service, team and assignee, one row per incident", + "tags": ["Incidents"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "incidents", + "getArgs": [ + { "key": "since", "value": "{{timeframe.start}}" }, + { "key": "until", "value": "{{timeframe.end}}" }, + { "key": "statuses[]", "value": "{{statuses?.length ? statuses : null}}" }, + { "key": "urgencies[]", "value": "{{urgencies?.length ? urgencies : null}}" }, + { "key": "service_ids[]", "value": "{{service?.map(o => o.rawId) || null}}" }, + { "key": "team_ids[]", "value": "{{team?.map(o => o.rawId) || null}}" }, + { "key": "user_ids[]", "value": "{{assignee?.map(o => o.rawId) || null}}" } + ], + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "incidents" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + }, + "postRequestScript": "incidents.js" + }, + "matches": "none", + "ui": [ + { + "type": "choiceChips", + "name": "statuses", + "label": "Status", + "isMulti": true, + "help": "Leave empty to include all statuses. Filtering here is done by PagerDuty rather than in the browser, keeping large result sets manageable.", + "options": [ + { "value": "triggered", "label": "Triggered" }, + { "value": "acknowledged", "label": "Acknowledged" }, + { "value": "resolved", "label": "Resolved" } + ] + }, + { + "type": "choiceChips", + "name": "urgencies", + "label": "Urgency", + "isMulti": true, + "help": "Leave empty to include all urgencies.", + "options": [ + { "value": "high", "label": "High" }, + { "value": "low", "label": "Low" } + ] + }, + { + "type": "objects", + "name": "service", + "label": "Service (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } + }, + { + "type": "objects", + "name": "team", + "label": "Team (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } + }, + { + "type": "objects", + "name": "assignee", + "label": "Assignee (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "incidentNumber", "displayName": "Number", "shape": "number" }, + { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "error": ["triggered"], + "warning": ["acknowledged"], + "success": ["resolved"] + } + } + ] + }, + { "name": "urgency", "displayName": "Urgency", "shape": "string" }, + { "name": "priorityName", "displayName": "Priority", "shape": "string" }, + { "name": "serviceId", "displayName": "Service ID", "shape": "string", "visible": false }, + { "name": "serviceName", "displayName": "Service", "shape": "string" }, + { "name": "escalationPolicyName", "displayName": "Escalation Policy", "shape": "string", "visible": false }, + { "name": "teamNames", "displayName": "Teams", "shape": "string" }, + { "name": "assigneeNames", "displayName": "Assignees", "shape": "string" }, + { "name": "createdAt", "displayName": "Created", "shape": "date", "role": "timestamp" }, + { "name": "lastStatusChangeAt", "displayName": "Last Status Change", "shape": "date" }, + { "name": "resolvedAt", "displayName": "Resolved", "shape": "date" }, + { "name": "htmlUrl", "displayName": "Link", "shape": ["url", { "label": "Open in PagerDuty" }] }, + { "sourceId": "serviceId", "sourceType": "PagerDuty Service", "name": "serviceName" } + ], + "timeframes": [ + "last1hour", + "last12hours", + "last24hours", + "last7days", + "last30days", + "thisMonth", + "thisQuarter", + "lastMonth", + "lastQuarter" + ], + "defaultTimeframe": "last24hours" +} diff --git a/plugins/PagerDuty/v1/dataStreams/notifications.json b/plugins/PagerDuty/v1/dataStreams/notifications.json new file mode 100644 index 00000000..dafcfe7b --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/notifications.json @@ -0,0 +1,60 @@ +{ + "name": "notifications", + "displayName": "Notifications", + "description": "Outbound notifications sent to responders, one row per notification", + "tags": ["Users", "Notifications"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "notifications", + "expandInnerObjects": true, + "getArgs": [ + { "key": "since", "value": "{{timeframe.start}}" }, + { "key": "until", "value": "{{timeframe.end}}" }, + { "key": "filter", "value": "{{channel || null}}" } + ], + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "notifications" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + } + }, + "matches": "none", + "pathToData": "notifications", + "ui": [ + { + "type": "objects", + "name": "user", + "label": "User (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } + }, + { + "type": "choiceChips", + "name": "channel", + "label": "Channel (optional)", + "options": [ + { "value": "sms_notification", "label": "SMS" }, + { "value": "email_notification", "label": "Email" }, + { "value": "phone_notification", "label": "Phone" }, + { "value": "push_notification", "label": "Push" } + ] + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "name": "type", "displayName": "Channel", "shape": "string" }, + { "name": "status", "displayName": "Status", "shape": "string" }, + { "name": "address", "displayName": "Address", "shape": "string" }, + { "name": "user.id", "displayName": "User ID", "shape": "string", "visible": false }, + { "name": "user.summary", "displayName": "User", "shape": "string", "role": "label" }, + { "name": "started_at", "displayName": "Sent", "shape": "date", "role": "timestamp" }, + { "sourceId": "user.id", "sourceType": "PagerDuty User", "name": "user.summary" } + ], + "timeframes": ["last1hour", "last12hours", "last24hours", "last7days", "last30days", "thisMonth", "lastMonth"], + "defaultTimeframe": "last24hours" +} diff --git a/plugins/PagerDuty/v1/dataStreams/onCall.json b/plugins/PagerDuty/v1/dataStreams/onCall.json new file mode 100644 index 00000000..33ee3343 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/onCall.json @@ -0,0 +1,71 @@ +{ + "name": "onCall", + "displayName": "On-Call", + "description": "Who is on call now and over an upcoming look-ahead window, one row per on-call shift", + "tags": ["On-call"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "oncalls", + "expandInnerObjects": true, + "getArgs": [ + { "key": "since", "value": "{{new Date().toISOString()}}" }, + { "key": "until", "value": "{{new Date(Date.now() + Number(lookAheadDays || 1) * 86400000).toISOString()}}" }, + { "key": "user_ids[]", "value": "{{user?.map(o => o.rawId) || null}}" }, + { "key": "schedule_ids[]", "value": "{{schedule?.map(o => o.rawId) || null}}" }, + { "key": "escalation_policy_ids[]", "value": "{{escalationPolicy?.map(o => o.rawId) || null}}" } + ], + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "oncalls" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + } + }, + "matches": "none", + "pathToData": "oncalls", + "ui": [ + { + "type": "number", + "name": "lookAheadDays", + "label": "Look-ahead window (days)", + "help": "How far into the future to check the on-call schedule. Defaults to 1 (who's on call right now).", + "defaultValue": 1 + }, + { + "type": "objects", + "name": "user", + "label": "User (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } + }, + { + "type": "objects", + "name": "schedule", + "label": "Schedule (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Schedule"] } } + }, + { + "type": "objects", + "name": "escalationPolicy", + "label": "Escalation Policy (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Escalation Policy"] } } + } + ], + "metadata": [ + { "name": "user.id", "displayName": "User ID", "shape": "string", "visible": false }, + { "name": "user.summary", "displayName": "User", "shape": "string", "role": "label" }, + { "name": "schedule.id", "displayName": "Schedule ID", "shape": "string", "visible": false }, + { "name": "schedule.summary", "displayName": "Schedule", "shape": "string" }, + { "name": "escalation_policy.id", "displayName": "Escalation Policy ID", "shape": "string", "visible": false }, + { "name": "escalation_policy.summary", "displayName": "Escalation Policy", "shape": "string" }, + { "name": "escalation_level", "displayName": "Escalation Level", "shape": "number" }, + { "name": "start", "displayName": "Start", "shape": "date", "role": "timestamp" }, + { "name": "end", "displayName": "End", "shape": "date" }, + { "sourceId": "user.id", "sourceType": "PagerDuty User", "name": "user.summary" } + ], + "timeframes": false +} diff --git a/plugins/PagerDuty/v1/dataStreams/responderMetrics.json b/plugins/PagerDuty/v1/dataStreams/responderMetrics.json new file mode 100644 index 00000000..c711e451 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/responderMetrics.json @@ -0,0 +1,64 @@ +{ + "name": "responderMetrics", + "displayName": "Responder Metrics", + "description": "Aggregated responder performance — incident load, mean time to acknowledge and on-call time, one row per responder", + "tags": ["Users", "Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "post", + "endpointPath": "analytics/metrics/responders/all", + "postBody": { + "filters": { + "date_range_start": "{{timeframe.start}}", + "date_range_end": "{{timeframe.end}}", + "urgency": "{{urgency || null}}", + "team_ids": "{{team?.map(o => o.rawId) || null}}", + "responder_ids": "{{user?.map(o => o.rawId) || null}}" + }, + "time_zone": "{{dataSource.timezone}}" + }, + "pathToData": "data" + }, + "matches": "none", + "ui": [ + { + "type": "radio", + "name": "urgency", + "label": "Urgency (optional)", + "options": [ + { "value": "high", "label": "High" }, + { "value": "low", "label": "Low" } + ] + }, + { + "type": "objects", + "name": "team", + "label": "Team (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } + }, + { + "type": "objects", + "name": "user", + "label": "Responder (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } + } + ], + "metadata": [ + { "name": "responder_id", "displayName": "Responder ID", "shape": "string", "visible": false }, + { "name": "responder_name", "displayName": "Responder", "shape": "string", "role": "label" }, + { "name": "team_name", "displayName": "Team", "shape": "string" }, + { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, + { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, + { "name": "total_interruptions", "displayName": "Interruptions", "shape": "number" }, + { "name": "total_notifications", "displayName": "Notifications", "shape": "number", "visible": false }, + { "name": "mean_time_to_acknowledge_seconds", "displayName": "Mean Time to Acknowledge", "shape": "seconds" }, + { "name": "mean_engaged_seconds", "displayName": "Mean Engaged Time", "shape": "seconds", "visible": false }, + { "name": "total_engaged_seconds", "displayName": "Total Engaged Time", "shape": "seconds", "visible": false }, + { "name": "total_seconds_on_call", "displayName": "Time On-Call", "shape": "seconds" }, + { "name": "total_seconds_on_call_level_1", "displayName": "Time On-Call (Level 1)", "shape": "seconds", "visible": false }, + { "name": "total_seconds_on_call_level_2_plus", "displayName": "Time On-Call (Level 2+)", "shape": "seconds", "visible": false }, + { "sourceId": "responder_id", "sourceType": "PagerDuty User", "name": "responder_name" } + ], + "timeframes": true, + "defaultTimeframe": "last7days" +} diff --git a/plugins/PagerDuty/v1/dataStreams/schedules.json b/plugins/PagerDuty/v1/dataStreams/schedules.json new file mode 100644 index 00000000..39ef5e85 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/schedules.json @@ -0,0 +1,31 @@ +{ + "name": "schedules", + "displayName": "Schedules (Import)", + "description": "One row per PagerDuty on-call schedule", + "tags": ["On-call"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "schedules", + "pathToData": "schedules", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "schedules" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + } + }, + "matches": "none", + "metadata": [ + { "name": "id", "shape": "string" }, + { "name": "name", "shape": "string" }, + { "name": "time_zone", "displayName": "Timezone", "shape": "string" }, + { "name": "description", "displayName": "Description", "shape": "string" } + ], + "timeframes": false, + "visibility": { "type": "hidden" } +} diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/incidents.js b/plugins/PagerDuty/v1/dataStreams/scripts/incidents.js new file mode 100644 index 00000000..a543a34b --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/scripts/incidents.js @@ -0,0 +1,22 @@ +const rows = data.incidents || []; + +result = rows.map((i) => ({ + id: i.id, + incidentNumber: i.incident_number, + title: i.title, + status: i.status, + urgency: i.urgency, + priorityName: i.priority ? i.priority.summary : "", + serviceId: i.service ? i.service.id : "", + serviceName: i.service ? i.service.summary : "", + escalationPolicyName: i.escalation_policy ? i.escalation_policy.summary : "", + teamNames: (i.teams || []).map((t) => t.summary).join(", "), + assigneeNames: (i.assignments || []) + .map((a) => a.assignee && a.assignee.summary) + .filter(Boolean) + .join(", "), + createdAt: i.created_at, + lastStatusChangeAt: i.last_status_change_at, + resolvedAt: i.resolved_at || "", + htmlUrl: i.html_url, +})); diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/services.js b/plugins/PagerDuty/v1/dataStreams/scripts/services.js new file mode 100644 index 00000000..b187cb95 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/scripts/services.js @@ -0,0 +1,22 @@ +// Optional `service` object-picker parameter (stream `ui` name "service"). +// Selected objects arrive at context.config.service as an ARRAY (multi-select), +// each rawId a single-element array. Empty/absent -> account-wide, no filter. +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const selected = (context.config && context.config.service) || []; +const selectedIds = new Set(selected.map((o) => unwrap(o.rawId)).filter(Boolean)); + +const rows = data.services || []; +const scoped = selectedIds.size ? rows.filter((s) => selectedIds.has(s.id)) : rows; + +result = scoped.map((s) => ({ + id: s.id, + name: s.name, + status: s.status, + description: s.description || "", + escalationPolicyId: s.escalation_policy ? s.escalation_policy.id : "", + escalationPolicyName: s.escalation_policy ? s.escalation_policy.summary : "", + teamIds: (s.teams || []).map((t) => t.id).join(","), + teamNames: (s.teams || []).map((t) => t.summary).join(", "), + alertCreation: s.alert_creation || "", + htmlUrl: s.html_url, +})); diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/users.js b/plugins/PagerDuty/v1/dataStreams/scripts/users.js new file mode 100644 index 00000000..d22ec8df --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/scripts/users.js @@ -0,0 +1,21 @@ +// Optional `user` object-picker parameter (stream `ui` name "user"). +// Selected objects arrive at context.config.user as an ARRAY (multi-select), +// each rawId a single-element array. Empty/absent -> account-wide, no filter. +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const selected = (context.config && context.config.user) || []; +const selectedIds = new Set(selected.map((o) => unwrap(o.rawId)).filter(Boolean)); + +const rows = data.users || []; +const scoped = selectedIds.size ? rows.filter((u) => selectedIds.has(u.id)) : rows; + +result = scoped.map((u) => ({ + id: u.id, + name: u.name, + email: u.email, + role: u.role, + jobTitle: u.job_title || "", + timezone: u.time_zone || "", + teamIds: (u.teams || []).map((t) => t.id).join(","), + teamNames: (u.teams || []).map((t) => t.summary).join(", "), + htmlUrl: u.html_url, +})); diff --git a/plugins/PagerDuty/v1/dataStreams/services.json b/plugins/PagerDuty/v1/dataStreams/services.json new file mode 100644 index 00000000..e94f6646 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/services.json @@ -0,0 +1,59 @@ +{ + "name": "services", + "displayName": "Services", + "description": "PagerDuty services with current status, escalation policy and team associations, one row per service", + "tags": ["Services"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "services", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "services" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + }, + "postRequestScript": "services.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "service", + "label": "Service (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": ["active"], + "warning": ["warning", "maintenance"], + "error": ["critical"], + "unmonitored": ["disabled"] + } + } + ] + }, + { "name": "description", "displayName": "Description", "shape": "string" }, + { "name": "escalationPolicyId", "displayName": "Escalation Policy ID", "shape": "string", "visible": false }, + { "name": "escalationPolicyName", "displayName": "Escalation Policy", "shape": "string" }, + { "name": "teamIds", "displayName": "Team IDs", "shape": "string", "visible": false }, + { "name": "teamNames", "displayName": "Teams", "shape": "string" }, + { "name": "alertCreation", "displayName": "Alert Creation", "shape": "string", "visible": false }, + { "name": "htmlUrl", "displayName": "Link", "shape": ["url", { "label": "Open in PagerDuty" }] }, + { "sourceId": "id", "sourceType": "PagerDuty Service", "name": "name" } + ], + "timeframes": false +} diff --git a/plugins/PagerDuty/v1/dataStreams/teams.json b/plugins/PagerDuty/v1/dataStreams/teams.json new file mode 100644 index 00000000..18a92395 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/teams.json @@ -0,0 +1,30 @@ +{ + "name": "teams", + "displayName": "Teams (Import)", + "description": "One row per PagerDuty team", + "tags": ["Teams"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "teams", + "pathToData": "teams", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "teams" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + } + }, + "matches": "none", + "metadata": [ + { "name": "id", "shape": "string" }, + { "name": "name", "shape": "string" }, + { "name": "description", "displayName": "Description", "shape": "string" } + ], + "timeframes": false, + "visibility": { "type": "hidden" } +} diff --git a/plugins/PagerDuty/v1/dataStreams/users.json b/plugins/PagerDuty/v1/dataStreams/users.json new file mode 100644 index 00000000..756bb590 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/users.json @@ -0,0 +1,44 @@ +{ + "name": "users", + "displayName": "Users", + "description": "PagerDuty user roster with role, contact and team associations, one row per user", + "tags": ["Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "users", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "offset": { + "mode": "row", + "rowCountIn": { "realm": "payloadArraySize", "path": "users" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "offset" } + }, + "postRequestScript": "users.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "user", + "label": "User (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } + } + ], + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "email", "displayName": "Email", "shape": "string" }, + { "name": "role", "displayName": "Role", "shape": "string" }, + { "name": "jobTitle", "displayName": "Job Title", "shape": "string" }, + { "name": "timezone", "displayName": "Timezone", "shape": "string", "visible": false }, + { "name": "teamIds", "displayName": "Team IDs", "shape": "string", "visible": false }, + { "name": "teamNames", "displayName": "Teams", "shape": "string" }, + { "name": "htmlUrl", "displayName": "Link", "shape": ["url", { "label": "Open in PagerDuty" }] }, + { "sourceId": "id", "sourceType": "PagerDuty User", "name": "name" } + ], + "timeframes": false +} diff --git a/plugins/PagerDuty/v1/dataStreams/validateAuth.json b/plugins/PagerDuty/v1/dataStreams/validateAuth.json new file mode 100644 index 00000000..ad8930b1 --- /dev/null +++ b/plugins/PagerDuty/v1/dataStreams/validateAuth.json @@ -0,0 +1,17 @@ +{ + "name": "validateAuth", + "displayName": "Authentication Validation", + "description": "Returns the calling account's enabled ability flags", + "tags": ["Utility"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "abilities" + }, + "matches": "none", + "metadata": [{ "pattern": ".*" }], + "timeframes": false, + "visibility": { + "type": "hidden" + } +} diff --git a/plugins/PagerDuty/v1/defaultContent/manifest.json b/plugins/PagerDuty/v1/defaultContent/manifest.json new file mode 100644 index 00000000..a321cd51 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/manifest.json @@ -0,0 +1,9 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "onCall", "type": "dashboard" }, + { "name": "servicePerspective", "type": "dashboard" }, + { "name": "userPerspective", "type": "dashboard" }, + { "name": "teamPerspective", "type": "dashboard" } + ] +} diff --git a/plugins/PagerDuty/v1/defaultContent/onCall.dash.json b/plugins/PagerDuty/v1/defaultContent/onCall.dash.json new file mode 100644 index 00000000..d7c4aea4 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/onCall.dash.json @@ -0,0 +1,122 @@ +{ + "name": "On-Call", + "schemaVersion": "1.5", + "timeframe": "last30days", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "af2ee963-8f1f-400c-8096-15b90e619e60", + "x": 0, + "y": 0, + "w": 2, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "On Call Now", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[onCall]}}", + "name": "onCall", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user.summary", "schedule.summary", "escalation_policy.summary", "escalation_level"], + "hiddenColumns": ["start", "end"] + } + } + } + } + }, + { + "i": "aadc00db-6dee-4646-a705-c80a2c7edae1", + "x": 2, + "y": 0, + "w": 2, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "On Call — Next 7 Days", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[onCall]}}", + "name": "onCall", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { "lookAheadDays": 7 } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user.summary", "schedule.summary", "escalation_policy.summary", "start", "end"], + "hiddenColumns": ["escalation_level"] + } + } + } + } + }, + { + "i": "2546b468-e0f3-42cd-a3dd-a5c9a3eb13da", + "x": 0, + "y": 4, + "w": 4, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "On-Call Time by Responder", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["total_seconds_on_call", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "responder_name", + "yAxisData": ["total_seconds_on_call"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Time on-call", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + } + ] + } +} diff --git a/plugins/PagerDuty/v1/defaultContent/overview.dash.json b/plugins/PagerDuty/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..d49b0f1f --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/overview.dash.json @@ -0,0 +1,299 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last7days", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "407b220f-5f82-44a3-bdac-0f914de10443", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Total Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_incident_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "bac1e487-341b-4bc3-b393-1021c8379e31", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Acknowledge", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_first_ack", "comparisonColumn": "none" } } + } + } + }, + { + "i": "c75fe14e-0736-44eb-ab6a-c5b0002d0ff4", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Resolve", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_resolve", "comparisonColumn": "none" } } + } + } + }, + { + "i": "24f1ba74-7985-4f5a-8fd9-3b61be83f034", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Escalations", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_escalation_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "9bd9299c-e2fc-4b21-8d17-34b63438ef43", + "x": 0, + "y": 2, + "w": 2, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incident Volume", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { "aggregateUnit": "day" } + }, + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "range_start", + "yAxisColumn": ["total_incident_count"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Incidents", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + }, + { + "i": "29f32962-a552-4874-b889-8634ee14596d", + "x": 2, + "y": 2, + "w": 2, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents by Status", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["status", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "ef478f65-dc08-47b7-b225-a726272fbe46", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Top Services by Incident Volume", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetricsBreakdown]}}", + "name": "incidentMetricsBreakdown", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { "groupBy": "services" }, + "sort": { "by": [["total_incident_count", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "service_name", + "yAxisData": ["total_incident_count"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Incidents", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + }, + { + "i": "f50ef53c-c8d9-4c24-9cb2-f38fca101cc0", + "x": 2, + "y": 5, + "w": 2, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "On-Call Now", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[onCall]}}", + "name": "onCall", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user.summary", "schedule.summary", "escalation_policy.summary", "escalation_level", "start", "end"], + "hiddenColumns": [] + } + } + } + } + }, + { + "i": "06921b74-c2e9-4b5e-a46f-386f9ac88157", + "x": 0, + "y": 8, + "w": 4, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "Services Health", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[services]}}", + "name": "services", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-blocks", + "config": { + "data-stream-blocks": { + "labelColumn": "name", + "stateColumn": "status", + "sublabel": "escalationPolicyName", + "linkColumn": "name", + "columns": 6 + } + } + } + } + } + ] + } +} diff --git a/plugins/PagerDuty/v1/defaultContent/scopes.json b/plugins/PagerDuty/v1/defaultContent/scopes.json new file mode 100644 index 00000000..6876ef69 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/scopes.json @@ -0,0 +1,32 @@ +[ + { + "name": "Services", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } }, + "variable": { + "name": "PagerDuty Service", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Users", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } }, + "variable": { + "name": "PagerDuty User", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Teams", + "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } }, + "variable": { + "name": "PagerDuty Team", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json b/plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json new file mode 100644 index 00000000..ef551767 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json @@ -0,0 +1,292 @@ +{ + "name": "Service", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": ["{{variables.[PagerDuty Service]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "6b73dc4b-6a51-4c23-9bcc-2be9850113e2", + "x": 0, + "y": 0, + "w": 1, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "Service Details", + "description": "", + "variables": ["{{variables.[PagerDuty Service]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { "id": "datastream-properties", "name": "properties" }, + "scope": { + "scope": "{{scopes.[Services]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[PagerDuty Service]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { "data-stream-table": { "transpose": true } } + } + } + }, + { + "i": "c5ca7d80-fa56-4835-b202-b98d52d134f7", + "x": 1, + "y": 0, + "w": 1, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "Service Status", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[services]}}", + "name": "services", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-blocks", + "config": { + "data-stream-blocks": { + "labelColumn": "name", + "stateColumn": "status", + "sublabel": "escalationPolicyName", + "linkColumn": "none", + "columns": 1 + } + } + } + } + }, + { + "i": "13465997-ceda-4f98-bc18-971a37455345", + "x": 2, + "y": 0, + "w": 2, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["title", "status", "urgency", "createdAt"], + "hiddenColumns": ["priorityName", "teamNames", "assigneeNames", "lastStatusChangeAt", "resolvedAt"] + } + } + } + } + }, + { + "i": "a75b22c9-9092-45e8-aa14-5214c165acb3", + "x": 0, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Total Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_incident_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "267abc04-5b24-4880-867e-63eae5e227ca", + "x": 1, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Acknowledge", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_first_ack", "comparisonColumn": "none" } } + } + } + }, + { + "i": "b7b5aa5b-6706-4a4c-94c0-f49bb44520ed", + "x": 2, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Resolve", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_resolve", "comparisonColumn": "none" } } + } + } + }, + { + "i": "f4d5f444-775b-43c4-8384-715d3a730cac", + "x": 3, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Escalations", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_escalation_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "06bb25db-4bf6-4be8-9ff4-d5c57b43e321", + "x": 0, + "y": 6, + "w": 4, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incident Volume", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "aggregateUnit": "day", + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "range_start", + "yAxisColumn": ["total_incident_count"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Incidents", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + } + ] + } +} diff --git a/plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json b/plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json new file mode 100644 index 00000000..d9626a22 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json @@ -0,0 +1,259 @@ +{ + "name": "Team", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": ["{{variables.[PagerDuty Team]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "8c24adbd-b3db-4c04-ba5a-f2a36c6feba2", + "x": 0, + "y": 0, + "w": 1, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "Team Details", + "description": "", + "variables": ["{{variables.[PagerDuty Team]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { "id": "datastream-properties", "name": "properties" }, + "scope": { + "scope": "{{scopes.[Teams]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[PagerDuty Team]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { "data-stream-table": { "transpose": true } } + } + } + }, + { + "i": "f0150467-0090-4ad1-b8f7-85b724a8fa93", + "x": 1, + "y": 0, + "w": 3, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["title", "status", "urgency", "serviceName", "createdAt"], + "hiddenColumns": ["priorityName", "escalationPolicyName", "teamNames", "assigneeNames", "lastStatusChangeAt", "resolvedAt"] + } + } + } + } + }, + { + "i": "3275a382-513c-4f25-80de-fa0fb98b1efe", + "x": 0, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Total Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_incident_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "a2efba9d-a311-4926-bb4e-cab9bc0d88ba", + "x": 1, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Acknowledge", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_first_ack", "comparisonColumn": "none" } } + } + } + }, + { + "i": "95ba432d-c9c3-42f7-ade6-4d5cf8f3d166", + "x": 2, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Resolve", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_seconds_to_resolve", "comparisonColumn": "none" } } + } + } + }, + { + "i": "6dfd9b3f-e6b2-43cf-ab8d-10eae2a5f450", + "x": 3, + "y": 4, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Escalations", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_escalation_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "39dca1d8-9e66-4a18-9791-088761bed050", + "x": 0, + "y": 6, + "w": 4, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents by Responder", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + }, + "sort": { "by": [["total_incident_count", "desc"]], "top": 10 } + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "responder_name", + "yAxisData": ["total_incident_count"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Incidents", + "showXAxisLabel": false, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + } + ] + } +} diff --git a/plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json b/plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json new file mode 100644 index 00000000..fcb25932 --- /dev/null +++ b/plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json @@ -0,0 +1,285 @@ +{ + "name": "User", + "schemaVersion": "1.5", + "timeframe": "last7days", + "variables": ["{{variables.[PagerDuty User]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "74f4cad0-e543-44d0-98e0-0a718ae1babb", + "x": 0, + "y": 0, + "w": 1, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "User Details", + "description": "", + "variables": ["{{variables.[PagerDuty User]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { "id": "datastream-properties", "name": "properties" }, + "scope": { + "scope": "{{scopes.[Users]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[PagerDuty User]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { "data-stream-table": { "transpose": true } } + } + } + }, + { + "i": "f3fcc9c8-f0ff-40fd-9c55-f76ef91ae609", + "x": 1, + "y": 0, + "w": 3, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "timeframe": "none", + "_type": "tile/data-stream", + "title": "On-Call Shifts (Next 7 Days)", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[onCall]}}", + "name": "onCall", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "lookAheadDays": 7, + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["schedule.summary", "escalation_policy.summary", "escalation_level", "start", "end"], + "hiddenColumns": [] + } + } + } + } + }, + { + "i": "e981dcef-e4c3-4bd5-83c3-73a70f32cf8f", + "x": 0, + "y": 4, + "w": 4, + "h": 4, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Assigned Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidents]}}", + "name": "incidents", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "assignee": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["title", "status", "urgency", "serviceName", "createdAt"], + "hiddenColumns": ["priorityName", "escalationPolicyName", "teamNames", "lastStatusChangeAt", "resolvedAt"] + } + } + } + } + }, + { + "i": "5e572584-78eb-4602-abc3-b358e038c12a", + "x": 0, + "y": 8, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Total Incidents", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_incident_count", "comparisonColumn": "none" } } + } + } + }, + { + "i": "3ffa7813-94e8-4270-bbde-9c291dde03a2", + "x": 1, + "y": 8, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Mean Time to Acknowledge", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "mean_time_to_acknowledge_seconds", "comparisonColumn": "none" } } + } + } + }, + { + "i": "931731df-d1ac-4d8e-b618-941dc439529b", + "x": 2, + "y": 8, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Interruptions", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_interruptions", "comparisonColumn": "none" } } + } + } + }, + { + "i": "e02f7cd5-1b60-4c94-82c7-9e49b576d3cc", + "x": 3, + "y": 8, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Time On-Call", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[responderMetrics]}}", + "name": "responderMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "total_seconds_on_call", "comparisonColumn": "none" } } + } + } + }, + { + "i": "3ab3c3fe-8c9f-4556-b0d2-001aa837b4aa", + "x": 0, + "y": 10, + "w": 4, + "h": 3, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Recent Notifications", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[notifications]}}", + "name": "notifications", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[PagerDuty User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["type", "status", "address", "started_at"], + "hiddenColumns": [] + } + } + } + } + } + ] + } +} diff --git a/plugins/PagerDuty/v1/docs/README.md b/plugins/PagerDuty/v1/docs/README.md new file mode 100644 index 00000000..4ec614ed --- /dev/null +++ b/plugins/PagerDuty/v1/docs/README.md @@ -0,0 +1,71 @@ +Monitor [PagerDuty](https://www.pagerduty.com) incidents, services, on-call +schedules and responder performance in SquaredUp, via the +[PagerDuty REST API](https://developer.pagerduty.com/api-reference). + +## Setup + +You will need a PagerDuty **REST API key**. + +1. Sign in to your [PagerDuty account](https://app.pagerduty.com). +2. For a key tied to your own user, go to **My Profile → User Settings** and + click **Create API User Token**. For a key that keeps working if your + account is ever removed, go to **Integrations → API Access Keys** instead + and click **Create New API Key** (requires admin permissions) — choose + **Read-only** unless you have a reason to grant write access. +3. Copy the generated key and paste it into the **API token** field. +4. Choose the **Service region** that matches your account (check + **Account Settings** in PagerDuty if you're unsure — most accounts are + **Global/US**). + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ----------------- | -------- | +| **API token** | Authenticates every request via the `Authorization: Token token=...` header. | PagerDuty → **My Profile → User Settings** (personal) or **Integrations → API Access Keys** (general access). | Yes | +| **Service region** | Selects the PagerDuty API host (`api.pagerduty.com` or `api.eu.pagerduty.com`) for your account. | PagerDuty → **Account Settings**. | Yes | +| **Timezone** | IANA timezone used to align time-based filters and aggregated results. | — | No | + +On save, the plugin validates the token by fetching your account's enabled +abilities; an invalid, expired, or revoked token fails setup with an +authentication error. + +## What this plugin monitors + +- **Incidents** — filterable by status, urgency, service, team and assignee, plus account-wide and per-service/team/escalation-policy aggregated analytics (MTTA, MTTR, escalation and interruption counts). +- **Services** — current status, escalation policy and team associations. +- **On-call** — who's on call now and over an upcoming look-ahead window, per user, schedule or escalation policy. +- **Users and responder performance** — roster, incident load, mean time to acknowledge, interruptions and time spent on-call. +- **Notifications** — outbound notifications sent to responders (SMS, email, phone, push). + +The out-of-the-box dashboards include an account-wide **Overview**, a dedicated **On-Call** dashboard, and a perspective for each **Service**, **User** and **Team**. + +## Data streams + +- **Services** — current status, escalation policy and team associations, one row per service. +- **Users** — role, contact and team associations, one row per user. +- **Incidents** — filterable by status, urgency, service, team and assignee, one row per incident. +- **Incident Metrics** — account-wide aggregated incident analytics, optionally bucketed by day, week or month. +- **Incident Metrics by Service/Team/Escalation Policy** — the same analytics grouped by dimension, one row per group. +- **Responder Metrics** — aggregated responder performance, one row per responder. +- **On-Call** — who is on call now and over an upcoming look-ahead window, one row per shift. +- **Notifications** — outbound notifications sent to responders, one row per notification. + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Service** | `GET /services` | A monitored PagerDuty service. | +| **User** | `GET /users` | A PagerDuty user who can respond to incidents. | +| **Team** | `GET /teams` | A group of users and services. | +| **Schedule** | `GET /schedules` | An on-call rotation schedule. | +| **Escalation Policy** | `GET /escalation_policies` | A set of escalation rules used by one or more services. | + +Team membership is stored as a property on each Service and User (`teamIds`/`teamNames`), not as a graph relationship — there is no direct drilldown from a Team to its member services or users. + +## Known limitations + +- **Incident search is capped at a 6-month window** — the **Incidents** stream's timeframe options stop at "Last quarter"; use **Incident Metrics** for longer-range trends (up to 1 year). +- **Notifications are capped at a 3-month window** — the **Notifications** stream's timeframe options stop at "Last month". +- **No direct Team → Service/User relationship** — the PagerDuty API only lists services/users *with* their team IDs, not teams *with* their members, so the Team perspective shows incidents and responder metrics for a team but not a services/users roster. +- **Rate limits** — PagerDuty enforces per-account API rate limits; very large estates may import or refresh more slowly. +- **Read-only** — the plugin never creates, modifies, or deletes anything in PagerDuty. diff --git a/plugins/PagerDuty/v1/icon.svg b/plugins/PagerDuty/v1/icon.svg new file mode 100644 index 00000000..3be364b5 --- /dev/null +++ b/plugins/PagerDuty/v1/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/plugins/PagerDuty/v1/indexDefinitions/default.json b/plugins/PagerDuty/v1/indexDefinitions/default.json new file mode 100644 index 00000000..26fb1baa --- /dev/null +++ b/plugins/PagerDuty/v1/indexDefinitions/default.json @@ -0,0 +1,66 @@ +{ + "steps": [ + { + "name": "services", + "dataStream": { "name": "services" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "PagerDuty Service" }, + "properties": [ + "status", + "description", + "escalationPolicyId", + "escalationPolicyName", + "teamIds", + "teamNames" + ] + } + }, + { + "name": "users", + "dataStream": { "name": "users" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "PagerDuty User" }, + "properties": ["email", "role", "jobTitle", "timezone", "teamIds", "teamNames"] + } + }, + { + "name": "teams", + "dataStream": { "name": "teams" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "PagerDuty Team" }, + "properties": ["description"] + } + }, + { + "name": "schedules", + "dataStream": { "name": "schedules" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "PagerDuty Schedule" }, + "properties": [{ "timezone": "time_zone" }, "description"] + } + }, + { + "name": "escalationPolicies", + "dataStream": { "name": "escalationPolicies" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "PagerDuty Escalation Policy" }, + "properties": [{ "numLoops": "num_loops" }, "teamIds", "teamNames"] + } + } + ] +} diff --git a/plugins/PagerDuty/v1/metadata.json b/plugins/PagerDuty/v1/metadata.json new file mode 100644 index 00000000..b3cf2497 --- /dev/null +++ b/plugins/PagerDuty/v1/metadata.json @@ -0,0 +1,39 @@ +{ + "name": "pagerduty", + "displayName": "PagerDuty", + "version": "1.0.0", + "author": { "name": "@vinbab", "type": "community" }, + "description": "Visualize and monitor PagerDuty incidents, services, on-call schedules and responder performance in SquaredUp.", + "category": "Alert Management", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": ["pagerduty", "incident management", "on-call", "alerting", "incident response"], + "objectTypes": ["PagerDuty Service", "PagerDuty User", "PagerDuty Team", "PagerDuty Schedule", "PagerDuty Escalation Policy"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/PagerDuty/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/PagerDuty/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "{{region === 'eu' ? 'https://api.eu.pagerduty.com' : 'https://api.pagerduty.com'}}", + "authMode": "none", + "headers": [ + { "key": "Authorization", "value": "Token token={{apiToken}}" }, + { "key": "Accept", "value": "application/vnd.pagerduty+json;version=2" } + ], + "queryArgs": [{ "key": "time_zone", "value": "{{timezone}}" }] + } + } +} diff --git a/plugins/PagerDuty/v1/ui.json b/plugins/PagerDuty/v1/ui.json new file mode 100644 index 00000000..1e29ee6a --- /dev/null +++ b/plugins/PagerDuty/v1/ui.json @@ -0,0 +1,30 @@ +[ + { + "type": "password", + "name": "apiToken", + "label": "API token", + "help": "A PagerDuty REST API key. Create one in **My Profile → User Settings → API Access** for a personal token, or **Integrations → API Access Keys** in the PagerDuty console for a general-access token that survives your account being removed.", + "placeholder": "Enter your API Token", + "validation": { "required": true } + }, + { + "type": "radio", + "name": "region", + "label": "Service region", + "help": "The PagerDuty service region your account is hosted in. Check **Account Settings** in the PagerDuty console if you're not sure.", + "defaultValue": "us", + "options": [ + { "value": "us", "label": "Global/US (api.pagerduty.com)" }, + { "value": "eu", "label": "EU (api.eu.pagerduty.com)" } + ], + "validation": { "required": true } + }, + { + "type": "text", + "name": "timezone", + "label": "Timezone", + "help": "IANA timezone used to align time-based filters and aggregations (e.g. `America/New_York`, `Europe/London`). Defaults to UTC.", + "defaultValue": "Etc/UTC", + "placeholder": "Etc/UTC" + } +] From 9e9326a4dd433d484727c20cef4c97fe67466d0a Mon Sep 17 00:00:00 2001 From: vinbab Date: Mon, 17 Aug 2026 16:46:15 +0100 Subject: [PATCH 2/8] Fix PagerDuty plugin bugs found in live testing, promote to v2 Live testing against an authenticated PagerDuty tenant surfaced several real bugs: escalation policy team columns crashed on every row, three analytics streams sent malformed nested-array filters that PagerDuty rejected, responder metrics called the wrong (aggregate-only) endpoint, the notifications user filter silently no-op'd, and a missing Teams account ability failed the entire import instead of just that step. Co-Authored-By: Claude Sonnet 5 --- .../{v1 => v2}/configValidation.json | 0 .../PagerDuty/{v1 => v2}/custom_types.json | 0 .../dataStreams/escalationPolicies.json | 19 +++--------------- .../dataStreams/incidentMetrics.json | 6 +++--- .../dataStreams/incidentMetricsBreakdown.json | 6 +++--- .../{v1 => v2}/dataStreams/incidents.json | 8 ++++---- .../{v1 => v2}/dataStreams/notifications.json | 6 ++---- .../{v1 => v2}/dataStreams/onCall.json | 8 ++++---- .../dataStreams/responderMetrics.json | 9 ++++----- .../{v1 => v2}/dataStreams/schedules.json | 0 .../dataStreams/scripts/escalationPolicies.js | 9 +++++++++ .../dataStreams/scripts/incidents.js | 0 .../v2/dataStreams/scripts/notifications.js | 20 +++++++++++++++++++ .../dataStreams/scripts/services.js | 0 .../{v1 => v2}/dataStreams/scripts/users.js | 0 .../{v1 => v2}/dataStreams/services.json | 0 .../{v1 => v2}/dataStreams/teams.json | 0 .../{v1 => v2}/dataStreams/users.json | 0 .../{v1 => v2}/dataStreams/validateAuth.json | 0 .../{v1 => v2}/defaultContent/manifest.json | 0 .../defaultContent/onCall.dash.json | 0 .../defaultContent/overview.dash.json | 0 .../{v1 => v2}/defaultContent/scopes.json | 0 .../servicePerspective.dash.json | 0 .../defaultContent/teamPerspective.dash.json | 0 .../defaultContent/userPerspective.dash.json | 0 plugins/PagerDuty/{v1 => v2}/docs/README.md | 2 ++ plugins/PagerDuty/{v1 => v2}/icon.svg | 0 .../{v1 => v2}/indexDefinitions/default.json | 3 ++- plugins/PagerDuty/{v1 => v2}/metadata.json | 6 +++--- plugins/PagerDuty/{v1 => v2}/ui.json | 0 31 files changed, 59 insertions(+), 43 deletions(-) rename plugins/PagerDuty/{v1 => v2}/configValidation.json (100%) rename plugins/PagerDuty/{v1 => v2}/custom_types.json (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/escalationPolicies.json (64%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/incidentMetrics.json (96%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/incidentMetricsBreakdown.json (95%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/incidents.json (93%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/notifications.json (92%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/onCall.json (91%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/responderMetrics.json (90%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/schedules.json (100%) create mode 100644 plugins/PagerDuty/v2/dataStreams/scripts/escalationPolicies.js rename plugins/PagerDuty/{v1 => v2}/dataStreams/scripts/incidents.js (100%) create mode 100644 plugins/PagerDuty/v2/dataStreams/scripts/notifications.js rename plugins/PagerDuty/{v1 => v2}/dataStreams/scripts/services.js (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/scripts/users.js (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/services.json (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/teams.json (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/users.json (100%) rename plugins/PagerDuty/{v1 => v2}/dataStreams/validateAuth.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/manifest.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/onCall.dash.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/overview.dash.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/scopes.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/servicePerspective.dash.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/teamPerspective.dash.json (100%) rename plugins/PagerDuty/{v1 => v2}/defaultContent/userPerspective.dash.json (100%) rename plugins/PagerDuty/{v1 => v2}/docs/README.md (85%) rename plugins/PagerDuty/{v1 => v2}/icon.svg (100%) rename plugins/PagerDuty/{v1 => v2}/indexDefinitions/default.json (97%) rename plugins/PagerDuty/{v1 => v2}/metadata.json (94%) rename plugins/PagerDuty/{v1 => v2}/ui.json (100%) diff --git a/plugins/PagerDuty/v1/configValidation.json b/plugins/PagerDuty/v2/configValidation.json similarity index 100% rename from plugins/PagerDuty/v1/configValidation.json rename to plugins/PagerDuty/v2/configValidation.json diff --git a/plugins/PagerDuty/v1/custom_types.json b/plugins/PagerDuty/v2/custom_types.json similarity index 100% rename from plugins/PagerDuty/v1/custom_types.json rename to plugins/PagerDuty/v2/custom_types.json diff --git a/plugins/PagerDuty/v1/dataStreams/escalationPolicies.json b/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json similarity index 64% rename from plugins/PagerDuty/v1/dataStreams/escalationPolicies.json rename to plugins/PagerDuty/v2/dataStreams/escalationPolicies.json index fa09164b..66243363 100644 --- a/plugins/PagerDuty/v1/dataStreams/escalationPolicies.json +++ b/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json @@ -7,7 +7,7 @@ "config": { "httpMethod": "get", "endpointPath": "escalation_policies", - "pathToData": "escalation_policies", + "postRequestScript": "escalationPolicies.js", "paging": { "mode": "offset", "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, @@ -24,21 +24,8 @@ { "name": "id", "shape": "string" }, { "name": "name", "shape": "string" }, { "name": "num_loops", "displayName": "Number of Loops", "shape": "number" }, - { "name": "teams", "visible": false }, - { - "name": "teamIds", - "displayName": "Team IDs", - "computed": true, - "valueExpression": "{{ ($['teams'] || []).map(t => t.id).join(',') }}", - "shape": "string" - }, - { - "name": "teamNames", - "displayName": "Team Names", - "computed": true, - "valueExpression": "{{ ($['teams'] || []).map(t => t.summary).join(', ') }}", - "shape": "string" - } + { "name": "teamIds", "displayName": "Team IDs", "shape": "string" }, + { "name": "teamNames", "displayName": "Team Names", "shape": "string" } ], "timeframes": false, "visibility": { "type": "hidden" } diff --git a/plugins/PagerDuty/v1/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json similarity index 96% rename from plugins/PagerDuty/v1/dataStreams/incidentMetrics.json rename to plugins/PagerDuty/v2/dataStreams/incidentMetrics.json index a817d6b0..54565d2c 100644 --- a/plugins/PagerDuty/v1/dataStreams/incidentMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json @@ -12,8 +12,8 @@ "created_at_start": "{{timeframe.start}}", "created_at_end": "{{timeframe.end}}", "urgency": "{{urgency || null}}", - "team_ids": "{{team?.map(o => o.rawId) || null}}", - "service_ids": "{{service?.map(o => o.rawId) || null}}" + "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", + "service_ids": "{{service?.flatMap(o => o.rawId) || null}}" }, "time_zone": "{{dataSource.timezone}}", "aggregate_unit": "{{aggregateUnit || null}}" @@ -80,5 +80,5 @@ { "name": "up_time_pct", "displayName": "Uptime", "shape": "percent" } ], "timeframes": true, - "defaultTimeframe": "last7days" + "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json similarity index 95% rename from plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json rename to plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json index a623f341..271ec416 100644 --- a/plugins/PagerDuty/v1/dataStreams/incidentMetricsBreakdown.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json @@ -12,8 +12,8 @@ "created_at_start": "{{timeframe.start}}", "created_at_end": "{{timeframe.end}}", "urgency": "{{urgency || null}}", - "team_ids": "{{team?.map(o => o.rawId) || null}}", - "service_ids": "{{service?.map(o => o.rawId) || null}}" + "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", + "service_ids": "{{service?.flatMap(o => o.rawId) || null}}" }, "time_zone": "{{dataSource.timezone}}" }, @@ -73,5 +73,5 @@ { "sourceId": "service_id", "sourceType": "PagerDuty Service", "name": "service_name" } ], "timeframes": true, - "defaultTimeframe": "last7days" + "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v1/dataStreams/incidents.json b/plugins/PagerDuty/v2/dataStreams/incidents.json similarity index 93% rename from plugins/PagerDuty/v1/dataStreams/incidents.json rename to plugins/PagerDuty/v2/dataStreams/incidents.json index 69d898ec..f7ed8b78 100644 --- a/plugins/PagerDuty/v1/dataStreams/incidents.json +++ b/plugins/PagerDuty/v2/dataStreams/incidents.json @@ -12,9 +12,9 @@ { "key": "until", "value": "{{timeframe.end}}" }, { "key": "statuses[]", "value": "{{statuses?.length ? statuses : null}}" }, { "key": "urgencies[]", "value": "{{urgencies?.length ? urgencies : null}}" }, - { "key": "service_ids[]", "value": "{{service?.map(o => o.rawId) || null}}" }, - { "key": "team_ids[]", "value": "{{team?.map(o => o.rawId) || null}}" }, - { "key": "user_ids[]", "value": "{{assignee?.map(o => o.rawId) || null}}" } + { "key": "service_ids[]", "value": "{{service?.flatMap(o => o.rawId) || null}}" }, + { "key": "team_ids[]", "value": "{{team?.flatMap(o => o.rawId) || null}}" }, + { "key": "user_ids[]", "value": "{{assignee?.flatMap(o => o.rawId) || null}}" } ], "paging": { "mode": "offset", @@ -114,5 +114,5 @@ "lastMonth", "lastQuarter" ], - "defaultTimeframe": "last24hours" + "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v1/dataStreams/notifications.json b/plugins/PagerDuty/v2/dataStreams/notifications.json similarity index 92% rename from plugins/PagerDuty/v1/dataStreams/notifications.json rename to plugins/PagerDuty/v2/dataStreams/notifications.json index dafcfe7b..40d8c92c 100644 --- a/plugins/PagerDuty/v1/dataStreams/notifications.json +++ b/plugins/PagerDuty/v2/dataStreams/notifications.json @@ -7,12 +7,12 @@ "config": { "httpMethod": "get", "endpointPath": "notifications", - "expandInnerObjects": true, "getArgs": [ { "key": "since", "value": "{{timeframe.start}}" }, { "key": "until", "value": "{{timeframe.end}}" }, { "key": "filter", "value": "{{channel || null}}" } ], + "postRequestScript": "notifications.js", "paging": { "mode": "offset", "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, @@ -25,7 +25,6 @@ } }, "matches": "none", - "pathToData": "notifications", "ui": [ { "type": "objects", @@ -48,7 +47,6 @@ "metadata": [ { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, { "name": "type", "displayName": "Channel", "shape": "string" }, - { "name": "status", "displayName": "Status", "shape": "string" }, { "name": "address", "displayName": "Address", "shape": "string" }, { "name": "user.id", "displayName": "User ID", "shape": "string", "visible": false }, { "name": "user.summary", "displayName": "User", "shape": "string", "role": "label" }, @@ -56,5 +54,5 @@ { "sourceId": "user.id", "sourceType": "PagerDuty User", "name": "user.summary" } ], "timeframes": ["last1hour", "last12hours", "last24hours", "last7days", "last30days", "thisMonth", "lastMonth"], - "defaultTimeframe": "last24hours" + "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v1/dataStreams/onCall.json b/plugins/PagerDuty/v2/dataStreams/onCall.json similarity index 91% rename from plugins/PagerDuty/v1/dataStreams/onCall.json rename to plugins/PagerDuty/v2/dataStreams/onCall.json index 33ee3343..9d76429c 100644 --- a/plugins/PagerDuty/v1/dataStreams/onCall.json +++ b/plugins/PagerDuty/v2/dataStreams/onCall.json @@ -7,13 +7,14 @@ "config": { "httpMethod": "get", "endpointPath": "oncalls", + "pathToData": "oncalls", "expandInnerObjects": true, "getArgs": [ { "key": "since", "value": "{{new Date().toISOString()}}" }, { "key": "until", "value": "{{new Date(Date.now() + Number(lookAheadDays || 1) * 86400000).toISOString()}}" }, - { "key": "user_ids[]", "value": "{{user?.map(o => o.rawId) || null}}" }, - { "key": "schedule_ids[]", "value": "{{schedule?.map(o => o.rawId) || null}}" }, - { "key": "escalation_policy_ids[]", "value": "{{escalationPolicy?.map(o => o.rawId) || null}}" } + { "key": "user_ids[]", "value": "{{user?.flatMap(o => o.rawId) || null}}" }, + { "key": "schedule_ids[]", "value": "{{schedule?.flatMap(o => o.rawId) || null}}" }, + { "key": "escalation_policy_ids[]", "value": "{{escalationPolicy?.flatMap(o => o.rawId) || null}}" } ], "paging": { "mode": "offset", @@ -27,7 +28,6 @@ } }, "matches": "none", - "pathToData": "oncalls", "ui": [ { "type": "number", diff --git a/plugins/PagerDuty/v1/dataStreams/responderMetrics.json b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json similarity index 90% rename from plugins/PagerDuty/v1/dataStreams/responderMetrics.json rename to plugins/PagerDuty/v2/dataStreams/responderMetrics.json index c711e451..14af811f 100644 --- a/plugins/PagerDuty/v1/dataStreams/responderMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json @@ -6,14 +6,14 @@ "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "post", - "endpointPath": "analytics/metrics/responders/all", + "endpointPath": "analytics/metrics/responders", "postBody": { "filters": { "date_range_start": "{{timeframe.start}}", "date_range_end": "{{timeframe.end}}", "urgency": "{{urgency || null}}", - "team_ids": "{{team?.map(o => o.rawId) || null}}", - "responder_ids": "{{user?.map(o => o.rawId) || null}}" + "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", + "responder_ids": "{{user?.flatMap(o => o.rawId) || null}}" }, "time_zone": "{{dataSource.timezone}}" }, @@ -46,7 +46,6 @@ "metadata": [ { "name": "responder_id", "displayName": "Responder ID", "shape": "string", "visible": false }, { "name": "responder_name", "displayName": "Responder", "shape": "string", "role": "label" }, - { "name": "team_name", "displayName": "Team", "shape": "string" }, { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, { "name": "total_interruptions", "displayName": "Interruptions", "shape": "number" }, @@ -60,5 +59,5 @@ { "sourceId": "responder_id", "sourceType": "PagerDuty User", "name": "responder_name" } ], "timeframes": true, - "defaultTimeframe": "last7days" + "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v1/dataStreams/schedules.json b/plugins/PagerDuty/v2/dataStreams/schedules.json similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/schedules.json rename to plugins/PagerDuty/v2/dataStreams/schedules.json diff --git a/plugins/PagerDuty/v2/dataStreams/scripts/escalationPolicies.js b/plugins/PagerDuty/v2/dataStreams/scripts/escalationPolicies.js new file mode 100644 index 00000000..97761ba3 --- /dev/null +++ b/plugins/PagerDuty/v2/dataStreams/scripts/escalationPolicies.js @@ -0,0 +1,9 @@ +const rows = data.escalation_policies || []; + +result = rows.map((p) => ({ + id: p.id, + name: p.name, + num_loops: p.num_loops, + teamIds: (p.teams || []).map((t) => t.id).join(","), + teamNames: (p.teams || []).map((t) => t.summary).join(", "), +})); diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/incidents.js b/plugins/PagerDuty/v2/dataStreams/scripts/incidents.js similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/scripts/incidents.js rename to plugins/PagerDuty/v2/dataStreams/scripts/incidents.js diff --git a/plugins/PagerDuty/v2/dataStreams/scripts/notifications.js b/plugins/PagerDuty/v2/dataStreams/scripts/notifications.js new file mode 100644 index 00000000..b70dc33d --- /dev/null +++ b/plugins/PagerDuty/v2/dataStreams/scripts/notifications.js @@ -0,0 +1,20 @@ +// Optional `user` object-picker parameter (stream `ui` name "user"). +// PagerDuty's GET /notifications endpoint has no server-side user filter, +// so scoping is done client-side here. Selected objects arrive at +// context.config.user as an ARRAY (multi-select), each rawId a single-element +// array. Empty/absent -> account-wide, no filter. +const unwrap = (v) => (Array.isArray(v) ? v[0] : v); +const selected = (context.config && context.config.user) || []; +const selectedIds = new Set(selected.map((o) => unwrap(o.rawId)).filter(Boolean)); + +const rows = data.notifications || []; +const scoped = selectedIds.size ? rows.filter((n) => n.user && selectedIds.has(n.user.id)) : rows; + +result = scoped.map((n) => ({ + id: n.id, + type: n.type, + address: n.address || "", + "user.id": n.user ? n.user.id : "", + "user.summary": n.user ? n.user.summary : "", + started_at: n.started_at, +})); diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/services.js b/plugins/PagerDuty/v2/dataStreams/scripts/services.js similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/scripts/services.js rename to plugins/PagerDuty/v2/dataStreams/scripts/services.js diff --git a/plugins/PagerDuty/v1/dataStreams/scripts/users.js b/plugins/PagerDuty/v2/dataStreams/scripts/users.js similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/scripts/users.js rename to plugins/PagerDuty/v2/dataStreams/scripts/users.js diff --git a/plugins/PagerDuty/v1/dataStreams/services.json b/plugins/PagerDuty/v2/dataStreams/services.json similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/services.json rename to plugins/PagerDuty/v2/dataStreams/services.json diff --git a/plugins/PagerDuty/v1/dataStreams/teams.json b/plugins/PagerDuty/v2/dataStreams/teams.json similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/teams.json rename to plugins/PagerDuty/v2/dataStreams/teams.json diff --git a/plugins/PagerDuty/v1/dataStreams/users.json b/plugins/PagerDuty/v2/dataStreams/users.json similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/users.json rename to plugins/PagerDuty/v2/dataStreams/users.json diff --git a/plugins/PagerDuty/v1/dataStreams/validateAuth.json b/plugins/PagerDuty/v2/dataStreams/validateAuth.json similarity index 100% rename from plugins/PagerDuty/v1/dataStreams/validateAuth.json rename to plugins/PagerDuty/v2/dataStreams/validateAuth.json diff --git a/plugins/PagerDuty/v1/defaultContent/manifest.json b/plugins/PagerDuty/v2/defaultContent/manifest.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/manifest.json rename to plugins/PagerDuty/v2/defaultContent/manifest.json diff --git a/plugins/PagerDuty/v1/defaultContent/onCall.dash.json b/plugins/PagerDuty/v2/defaultContent/onCall.dash.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/onCall.dash.json rename to plugins/PagerDuty/v2/defaultContent/onCall.dash.json diff --git a/plugins/PagerDuty/v1/defaultContent/overview.dash.json b/plugins/PagerDuty/v2/defaultContent/overview.dash.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/overview.dash.json rename to plugins/PagerDuty/v2/defaultContent/overview.dash.json diff --git a/plugins/PagerDuty/v1/defaultContent/scopes.json b/plugins/PagerDuty/v2/defaultContent/scopes.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/scopes.json rename to plugins/PagerDuty/v2/defaultContent/scopes.json diff --git a/plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/servicePerspective.dash.json rename to plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json diff --git a/plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/teamPerspective.dash.json rename to plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json diff --git a/plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/userPerspective.dash.json similarity index 100% rename from plugins/PagerDuty/v1/defaultContent/userPerspective.dash.json rename to plugins/PagerDuty/v2/defaultContent/userPerspective.dash.json diff --git a/plugins/PagerDuty/v1/docs/README.md b/plugins/PagerDuty/v2/docs/README.md similarity index 85% rename from plugins/PagerDuty/v1/docs/README.md rename to plugins/PagerDuty/v2/docs/README.md index 4ec614ed..a10ab69a 100644 --- a/plugins/PagerDuty/v1/docs/README.md +++ b/plugins/PagerDuty/v2/docs/README.md @@ -66,6 +66,8 @@ Team membership is stored as a property on each Service and User (`teamIds`/`tea - **Incident search is capped at a 6-month window** — the **Incidents** stream's timeframe options stop at "Last quarter"; use **Incident Metrics** for longer-range trends (up to 1 year). - **Notifications are capped at a 3-month window** — the **Notifications** stream's timeframe options stop at "Last month". +- **Teams require an add-on account ability** — the **Teams** import step (and the Team scope/dashboards it feeds) only returns data on PagerDuty plans with the Teams ability enabled. On accounts without it, PagerDuty rejects the request and the step is skipped with a warning rather than failing the whole import — everything else (Services, Users, Schedules, Escalation Policies, Incidents, etc.) still imports and refreshes normally. +- **Notifications' User filter is applied client-side** — PagerDuty's notifications endpoint has no server-side user filter, so the plugin fetches the full window and filters by user in the plugin itself. This is transparent to you, but very high notification volumes over a wide timeframe may be slower to filter than a server-side filter would be. - **No direct Team → Service/User relationship** — the PagerDuty API only lists services/users *with* their team IDs, not teams *with* their members, so the Team perspective shows incidents and responder metrics for a team but not a services/users roster. - **Rate limits** — PagerDuty enforces per-account API rate limits; very large estates may import or refresh more slowly. - **Read-only** — the plugin never creates, modifies, or deletes anything in PagerDuty. diff --git a/plugins/PagerDuty/v1/icon.svg b/plugins/PagerDuty/v2/icon.svg similarity index 100% rename from plugins/PagerDuty/v1/icon.svg rename to plugins/PagerDuty/v2/icon.svg diff --git a/plugins/PagerDuty/v1/indexDefinitions/default.json b/plugins/PagerDuty/v2/indexDefinitions/default.json similarity index 97% rename from plugins/PagerDuty/v1/indexDefinitions/default.json rename to plugins/PagerDuty/v2/indexDefinitions/default.json index 26fb1baa..72ab7383 100644 --- a/plugins/PagerDuty/v1/indexDefinitions/default.json +++ b/plugins/PagerDuty/v2/indexDefinitions/default.json @@ -38,7 +38,8 @@ "name": "name", "type": { "value": "PagerDuty Team" }, "properties": ["description"] - } + }, + "optional": true }, { "name": "schedules", diff --git a/plugins/PagerDuty/v1/metadata.json b/plugins/PagerDuty/v2/metadata.json similarity index 94% rename from plugins/PagerDuty/v1/metadata.json rename to plugins/PagerDuty/v2/metadata.json index b3cf2497..9f72518c 100644 --- a/plugins/PagerDuty/v1/metadata.json +++ b/plugins/PagerDuty/v2/metadata.json @@ -1,7 +1,7 @@ { "name": "pagerduty", "displayName": "PagerDuty", - "version": "1.0.0", + "version": "2.0.0", "author": { "name": "@vinbab", "type": "community" }, "description": "Visualize and monitor PagerDuty incidents, services, on-call schedules and responder performance in SquaredUp.", "category": "Alert Management", @@ -14,12 +14,12 @@ "links": [ { "category": "documentation", - "url": "https://github.com/squaredup/plugins/blob/main/plugins/PagerDuty/v1/docs/README.md", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/PagerDuty/v2/docs/README.md", "label": "Help adding this plugin" }, { "category": "source", - "url": "https://github.com/squaredup/plugins/tree/main/plugins/PagerDuty/v1", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/PagerDuty/v2", "label": "Repository" } ], diff --git a/plugins/PagerDuty/v1/ui.json b/plugins/PagerDuty/v2/ui.json similarity index 100% rename from plugins/PagerDuty/v1/ui.json rename to plugins/PagerDuty/v2/ui.json From 1328e93f6e1f659d31e29867aaebbca914ddaaa4 Mon Sep 17 00:00:00 2001 From: vinbab Date: Tue, 8 Sep 2026 13:18:03 +0100 Subject: [PATCH 3/8] Address customer feedback on Incidents, Incident Metrics and Responder Metrics Incidents: add a Modified By column for resolution attribution, and let This Year/Last Year/custom ranges be selected by auto-clamping to PagerDuty's ~6-month incident search cap instead of hiding those options. Incident Metrics (account-wide and by Service/Team/Escalation Policy): add an "Incidents Minus Auto-Resolved" metric, and surface it as a new OOB tile on the Overview, Service and Team dashboards. Responder Metrics: document that PagerDuty's responder analytics API has no resolution-count field, and point to the Incidents stream's new Modified By column as the per-incident alternative. Co-Authored-By: Claude Sonnet 5 --- .../v2/dataStreams/incidentMetrics.json | 7 ++++ .../dataStreams/incidentMetricsBreakdown.json | 1 + .../PagerDuty/v2/dataStreams/incidents.json | 19 ++++------- .../v2/dataStreams/scripts/incidents.js | 3 ++ .../v2/defaultContent/overview.dash.json | 25 +++++++++++++++ .../servicePerspective.dash.json | 32 +++++++++++++++++++ .../defaultContent/teamPerspective.dash.json | 32 +++++++++++++++++++ plugins/PagerDuty/v2/docs/README.md | 5 +-- 8 files changed, 109 insertions(+), 15 deletions(-) diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json index 54565d2c..b402a299 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json @@ -60,6 +60,13 @@ { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, { "name": "total_incidents_auto_resolved", "displayName": "Auto-Resolved", "shape": "number" }, + { + "name": "manuallyResolvedCount", + "displayName": "Incidents Minus Auto-Resolved", + "computed": true, + "valueExpression": "{{ ($['total_incident_count'] || 0) - ($['total_incidents_auto_resolved'] || 0) }}", + "shape": ["number", { "thousandsSeparator": true }] + }, { "name": "total_incidents_manual_escalated", "displayName": "Manually Escalated", "shape": "number" }, { "name": "total_incidents_timeout_escalated", "displayName": "Timeout-Escalated", "shape": "number" }, { "name": "total_incidents_reassigned", "displayName": "Reassigned", "shape": "number" }, diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json index 271ec416..dcaa12fb 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json @@ -65,6 +65,7 @@ { "name": "distinct_responder_count", "displayName": "Distinct Responders", "shape": "number" }, { "name": "total_incident_count", "displayName": "Incidents", "shape": ["number", { "thousandsSeparator": true }], "role": "value" }, { "name": "total_incidents_acknowledged", "displayName": "Acknowledged", "shape": "number" }, + { "name": "total_incidents_auto_resolved", "displayName": "Auto-Resolved", "shape": "number" }, { "name": "total_escalation_count", "displayName": "Escalations", "shape": "number" }, { "name": "total_interruptions", "displayName": "Interruptions", "shape": "number" }, { "name": "mean_seconds_to_first_ack", "displayName": "Mean Time to Acknowledge", "shape": "seconds" }, diff --git a/plugins/PagerDuty/v2/dataStreams/incidents.json b/plugins/PagerDuty/v2/dataStreams/incidents.json index f7ed8b78..bb16061a 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidents.json +++ b/plugins/PagerDuty/v2/dataStreams/incidents.json @@ -1,14 +1,14 @@ { "name": "incidents", "displayName": "Incidents", - "description": "PagerDuty incidents filterable by status, urgency, service, team and assignee, one row per incident", + "description": "PagerDuty incidents filterable by status, urgency, service, team and assignee, one row per incident. PagerDuty's incident search only supports a ~6-month lookback, so a longer or custom range is automatically clamped to the most recent 6 months.", "tags": ["Incidents"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", "endpointPath": "incidents", "getArgs": [ - { "key": "since", "value": "{{timeframe.start}}" }, + { "key": "since", "value": "{{new Date(Math.max(new Date(timeframe.start).getTime(), new Date(timeframe.end).getTime() - 182 * 86400000)).toISOString()}}" }, { "key": "until", "value": "{{timeframe.end}}" }, { "key": "statuses[]", "value": "{{statuses?.length ? statuses : null}}" }, { "key": "urgencies[]", "value": "{{urgencies?.length ? urgencies : null}}" }, @@ -97,22 +97,15 @@ { "name": "escalationPolicyName", "displayName": "Escalation Policy", "shape": "string", "visible": false }, { "name": "teamNames", "displayName": "Teams", "shape": "string" }, { "name": "assigneeNames", "displayName": "Assignees", "shape": "string" }, + { "name": "modifiedById", "displayName": "Modified By ID", "shape": "string", "visible": false }, + { "name": "modifiedByName", "displayName": "Modified By", "shape": "string" }, + { "name": "modifiedByType", "displayName": "Modified By Type", "shape": "string", "visible": false }, { "name": "createdAt", "displayName": "Created", "shape": "date", "role": "timestamp" }, { "name": "lastStatusChangeAt", "displayName": "Last Status Change", "shape": "date" }, { "name": "resolvedAt", "displayName": "Resolved", "shape": "date" }, { "name": "htmlUrl", "displayName": "Link", "shape": ["url", { "label": "Open in PagerDuty" }] }, { "sourceId": "serviceId", "sourceType": "PagerDuty Service", "name": "serviceName" } ], - "timeframes": [ - "last1hour", - "last12hours", - "last24hours", - "last7days", - "last30days", - "thisMonth", - "thisQuarter", - "lastMonth", - "lastQuarter" - ], + "timeframes": true, "defaultTimeframe": "dashboard" } diff --git a/plugins/PagerDuty/v2/dataStreams/scripts/incidents.js b/plugins/PagerDuty/v2/dataStreams/scripts/incidents.js index a543a34b..adc73a26 100644 --- a/plugins/PagerDuty/v2/dataStreams/scripts/incidents.js +++ b/plugins/PagerDuty/v2/dataStreams/scripts/incidents.js @@ -15,6 +15,9 @@ result = rows.map((i) => ({ .map((a) => a.assignee && a.assignee.summary) .filter(Boolean) .join(", "), + modifiedById: i.last_status_change_by ? i.last_status_change_by.id : "", + modifiedByName: i.last_status_change_by ? i.last_status_change_by.summary : "", + modifiedByType: i.last_status_change_by ? i.last_status_change_by.type : "", createdAt: i.created_at, lastStatusChangeAt: i.last_status_change_at, resolvedAt: i.resolved_at || "", diff --git a/plugins/PagerDuty/v2/defaultContent/overview.dash.json b/plugins/PagerDuty/v2/defaultContent/overview.dash.json index d49b0f1f..c792cf73 100644 --- a/plugins/PagerDuty/v2/defaultContent/overview.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/overview.dash.json @@ -293,6 +293,31 @@ } } } + }, + { + "i": "e2976967-d5d0-4893-bc17-8854f82d938d", + "x": 0, + "y": 11, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents minus auto-resolved", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + } + } } ] } diff --git a/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json index ef551767..fb7eeae0 100644 --- a/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json @@ -286,6 +286,38 @@ } } } + }, + { + "i": "f4599a51-5c19-439c-98eb-bb31eb7382f0", + "x": 0, + "y": 9, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents minus auto-resolved", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "service": { + "variable": "{{variables.[PagerDuty Service]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Services]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + } + } } ] } diff --git a/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json index d9626a22..44ab6c9f 100644 --- a/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json @@ -253,6 +253,38 @@ } } } + }, + { + "i": "7e13b2c1-55b1-45a0-8488-4f32f8358bf7", + "x": 0, + "y": 9, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents minus auto-resolved", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + } + } } ] } diff --git a/plugins/PagerDuty/v2/docs/README.md b/plugins/PagerDuty/v2/docs/README.md index a10ab69a..cea4d163 100644 --- a/plugins/PagerDuty/v2/docs/README.md +++ b/plugins/PagerDuty/v2/docs/README.md @@ -43,7 +43,7 @@ The out-of-the-box dashboards include an account-wide **Overview**, a dedicated - **Services** — current status, escalation policy and team associations, one row per service. - **Users** — role, contact and team associations, one row per user. -- **Incidents** — filterable by status, urgency, service, team and assignee, one row per incident. +- **Incidents** — filterable by status, urgency, service, team and assignee, one row per incident, including who last modified/resolved it. - **Incident Metrics** — account-wide aggregated incident analytics, optionally bucketed by day, week or month. - **Incident Metrics by Service/Team/Escalation Policy** — the same analytics grouped by dimension, one row per group. - **Responder Metrics** — aggregated responder performance, one row per responder. @@ -64,7 +64,8 @@ Team membership is stored as a property on each Service and User (`teamIds`/`tea ## Known limitations -- **Incident search is capped at a 6-month window** — the **Incidents** stream's timeframe options stop at "Last quarter"; use **Incident Metrics** for longer-range trends (up to 1 year). +- **Incident search is capped at a 6-month window** — this is a hard limit of PagerDuty's own incident search API, not a SquaredUp restriction: a request spanning more than ~6 months is rejected outright. The **Incidents** stream offers every timeframe, including This Year, Last Year and a custom range, but automatically clamps the start of any wider selection to 6 months before the end so the query never fails — you'll silently get the most recent 6 months of a longer range. Use **Incident Metrics** for longer-range trend analysis, which has no such cap. +- **Responder-level resolution counts aren't available** — PagerDuty's responder analytics API reports involvement (incident count, interruptions, acknowledgements, on-call time, mean time to resolve) but not how many incidents each responder actually resolved. For per-incident resolution attribution, use the **Incidents** stream's **Modified By** column, filtered to Status = Resolved. - **Notifications are capped at a 3-month window** — the **Notifications** stream's timeframe options stop at "Last month". - **Teams require an add-on account ability** — the **Teams** import step (and the Team scope/dashboards it feeds) only returns data on PagerDuty plans with the Teams ability enabled. On accounts without it, PagerDuty rejects the request and the step is skipped with a warning rather than failing the whole import — everything else (Services, Users, Schedules, Escalation Policies, Incidents, etc.) still imports and refreshes normally. - **Notifications' User filter is applied client-side** — PagerDuty's notifications endpoint has no server-side user filter, so the plugin fetches the full window and filters by user in the plugin itself. This is transparent to you, but very high notification volumes over a wide timeframe may be slower to filter than a server-side filter would be. From 0987051a57dd61fa129770c1c4e7ecd2fceefd67 Mon Sep 17 00:00:00 2001 From: vinbab Date: Tue, 8 Sep 2026 17:20:48 +0100 Subject: [PATCH 4/8] UI tweaks across setup screen, data streams and OOB dashboards Setup: generic tooltips with no PagerDuty portal navigation paths (avoids drift if the portal changes), clearer API token placeholder. Data streams: drop tags/categories, rename the breakdown stream to "Incident Metrics (by Service / Team / Escalation Policy)", drop "(optional)" from field labels, add placeholders to object pickers. Urgency/Channel filters (Incident Metrics, Incident Metrics by dimension, Responder Metrics, Notifications) move from radio/choiceChips to a clearable switch, matching Bucket by/Group by - confirmed live that PagerDuty's analytics API and the notifications endpoint both reject multiple values for these filters, so a true multi-select would be misleading. Incidents' Status/Urgency move to a fixed-list multi-select autocomplete (PagerDuty's /incidents endpoint does support multiple values here), consistent with the Jira/JQL pattern. OOB dashboards: move the "Incidents minus auto-resolved" tile from an isolated last row into the KPI row area on Overview, Service and Team, shifting everything below down to keep the same order. Co-Authored-By: Claude Sonnet 5 --- .../v2/dataStreams/escalationPolicies.json | 1 - .../v2/dataStreams/incidentMetrics.json | 11 ++-- .../dataStreams/incidentMetricsBreakdown.json | 13 ++-- .../PagerDuty/v2/dataStreams/incidents.json | 42 +++++++----- .../v2/dataStreams/notifications.json | 8 +-- plugins/PagerDuty/v2/dataStreams/onCall.json | 11 ++-- .../v2/dataStreams/responderMetrics.json | 11 ++-- .../PagerDuty/v2/dataStreams/schedules.json | 1 - .../PagerDuty/v2/dataStreams/services.json | 4 +- plugins/PagerDuty/v2/dataStreams/teams.json | 1 - plugins/PagerDuty/v2/dataStreams/users.json | 4 +- .../v2/dataStreams/validateAuth.json | 1 - .../v2/defaultContent/overview.dash.json | 60 ++++++++--------- .../servicePerspective.dash.json | 50 +++++++------- .../defaultContent/teamPerspective.dash.json | 66 +++++++++---------- plugins/PagerDuty/v2/docs/README.md | 2 +- plugins/PagerDuty/v2/ui.json | 6 +- 17 files changed, 153 insertions(+), 139 deletions(-) diff --git a/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json b/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json index 66243363..cf42dc33 100644 --- a/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json +++ b/plugins/PagerDuty/v2/dataStreams/escalationPolicies.json @@ -2,7 +2,6 @@ "name": "escalationPolicies", "displayName": "Escalation Policies (Import)", "description": "One row per PagerDuty escalation policy with its team associations", - "tags": ["Escalation"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json index b402a299..7773e081 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json @@ -2,7 +2,6 @@ "name": "incidentMetrics", "displayName": "Incident Metrics", "description": "Account-wide aggregated incident analytics — MTTA, MTTR, escalation and interruption counts, optionally bucketed by day, week or month", - "tags": ["Incidents", "Analytics"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "post", @@ -34,9 +33,9 @@ ] }, { - "type": "radio", + "type": "switch", "name": "urgency", - "label": "Urgency (optional)", + "label": "Urgency", "options": [ { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } @@ -45,13 +44,15 @@ { "type": "objects", "name": "service", - "label": "Service (optional)", + "label": "Service", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } }, { "type": "objects", "name": "team", - "label": "Team (optional)", + "label": "Team", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json index dcaa12fb..bab8fdb9 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json @@ -1,8 +1,7 @@ { "name": "incidentMetricsBreakdown", - "displayName": "Incident Metrics by Service/Team/Escalation Policy", + "displayName": "Incident Metrics (by Service / Team / Escalation Policy)", "description": "Aggregated incident analytics grouped by service, team or escalation policy, one row per group", - "tags": ["Incidents", "Analytics"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "post", @@ -34,9 +33,9 @@ "validation": { "required": true } }, { - "type": "radio", + "type": "switch", "name": "urgency", - "label": "Urgency (optional)", + "label": "Urgency", "options": [ { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } @@ -45,13 +44,15 @@ { "type": "objects", "name": "service", - "label": "Service (optional)", + "label": "Service", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } }, { "type": "objects", "name": "team", - "label": "Team (optional)", + "label": "Team", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/incidents.json b/plugins/PagerDuty/v2/dataStreams/incidents.json index bb16061a..a14201f6 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidents.json +++ b/plugins/PagerDuty/v2/dataStreams/incidents.json @@ -2,7 +2,6 @@ "name": "incidents", "displayName": "Incidents", "description": "PagerDuty incidents filterable by status, urgency, service, team and assignee, one row per incident. PagerDuty's incident search only supports a ~6-month lookback, so a longer or custom range is automatically clamped to the most recent 6 months.", - "tags": ["Incidents"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", @@ -31,44 +30,57 @@ "matches": "none", "ui": [ { - "type": "choiceChips", + "type": "autocomplete", "name": "statuses", "label": "Status", "isMulti": true, + "allowCustomValues": false, + "placeholder": "Select...", "help": "Leave empty to include all statuses. Filtering here is done by PagerDuty rather than in the browser, keeping large result sets manageable.", - "options": [ - { "value": "triggered", "label": "Triggered" }, - { "value": "acknowledged", "label": "Acknowledged" }, - { "value": "resolved", "label": "Resolved" } - ] + "data": { + "source": "fixed", + "values": [ + { "value": "triggered", "label": "Triggered" }, + { "value": "acknowledged", "label": "Acknowledged" }, + { "value": "resolved", "label": "Resolved" } + ] + } }, { - "type": "choiceChips", + "type": "autocomplete", "name": "urgencies", "label": "Urgency", "isMulti": true, + "allowCustomValues": false, + "placeholder": "Select...", "help": "Leave empty to include all urgencies.", - "options": [ - { "value": "high", "label": "High" }, - { "value": "low", "label": "Low" } - ] + "data": { + "source": "fixed", + "values": [ + { "value": "high", "label": "High" }, + { "value": "low", "label": "Low" } + ] + } }, { "type": "objects", "name": "service", - "label": "Service (optional)", + "label": "Service", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } }, { "type": "objects", "name": "team", - "label": "Team (optional)", + "label": "Team", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } }, { "type": "objects", "name": "assignee", - "label": "Assignee (optional)", + "label": "Assignee", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/notifications.json b/plugins/PagerDuty/v2/dataStreams/notifications.json index 40d8c92c..ffc2ec99 100644 --- a/plugins/PagerDuty/v2/dataStreams/notifications.json +++ b/plugins/PagerDuty/v2/dataStreams/notifications.json @@ -2,7 +2,6 @@ "name": "notifications", "displayName": "Notifications", "description": "Outbound notifications sent to responders, one row per notification", - "tags": ["Users", "Notifications"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", @@ -29,13 +28,14 @@ { "type": "objects", "name": "user", - "label": "User (optional)", + "label": "User", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } }, { - "type": "choiceChips", + "type": "switch", "name": "channel", - "label": "Channel (optional)", + "label": "Channel", "options": [ { "value": "sms_notification", "label": "SMS" }, { "value": "email_notification", "label": "Email" }, diff --git a/plugins/PagerDuty/v2/dataStreams/onCall.json b/plugins/PagerDuty/v2/dataStreams/onCall.json index 9d76429c..797e2258 100644 --- a/plugins/PagerDuty/v2/dataStreams/onCall.json +++ b/plugins/PagerDuty/v2/dataStreams/onCall.json @@ -2,7 +2,6 @@ "name": "onCall", "displayName": "On-Call", "description": "Who is on call now and over an upcoming look-ahead window, one row per on-call shift", - "tags": ["On-call"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", @@ -34,24 +33,28 @@ "name": "lookAheadDays", "label": "Look-ahead window (days)", "help": "How far into the future to check the on-call schedule. Defaults to 1 (who's on call right now).", + "placeholder": "Enter...", "defaultValue": 1 }, { "type": "objects", "name": "user", - "label": "User (optional)", + "label": "User", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } }, { "type": "objects", "name": "schedule", - "label": "Schedule (optional)", + "label": "Schedule", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Schedule"] } } }, { "type": "objects", "name": "escalationPolicy", - "label": "Escalation Policy (optional)", + "label": "Escalation Policy", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Escalation Policy"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json index 14af811f..f531875c 100644 --- a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json @@ -2,7 +2,6 @@ "name": "responderMetrics", "displayName": "Responder Metrics", "description": "Aggregated responder performance — incident load, mean time to acknowledge and on-call time, one row per responder", - "tags": ["Users", "Analytics"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "post", @@ -22,9 +21,9 @@ "matches": "none", "ui": [ { - "type": "radio", + "type": "switch", "name": "urgency", - "label": "Urgency (optional)", + "label": "Urgency", "options": [ { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } @@ -33,13 +32,15 @@ { "type": "objects", "name": "team", - "label": "Team (optional)", + "label": "Team", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Team"] } } }, { "type": "objects", "name": "user", - "label": "Responder (optional)", + "label": "Responder", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/schedules.json b/plugins/PagerDuty/v2/dataStreams/schedules.json index 39ef5e85..7a7f8d3b 100644 --- a/plugins/PagerDuty/v2/dataStreams/schedules.json +++ b/plugins/PagerDuty/v2/dataStreams/schedules.json @@ -2,7 +2,6 @@ "name": "schedules", "displayName": "Schedules (Import)", "description": "One row per PagerDuty on-call schedule", - "tags": ["On-call"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", diff --git a/plugins/PagerDuty/v2/dataStreams/services.json b/plugins/PagerDuty/v2/dataStreams/services.json index e94f6646..36ab3eb8 100644 --- a/plugins/PagerDuty/v2/dataStreams/services.json +++ b/plugins/PagerDuty/v2/dataStreams/services.json @@ -2,7 +2,6 @@ "name": "services", "displayName": "Services", "description": "PagerDuty services with current status, escalation policy and team associations, one row per service", - "tags": ["Services"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", @@ -24,7 +23,8 @@ { "type": "objects", "name": "service", - "label": "Service (optional)", + "label": "Service", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty Service"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/teams.json b/plugins/PagerDuty/v2/dataStreams/teams.json index 18a92395..af4c4d2f 100644 --- a/plugins/PagerDuty/v2/dataStreams/teams.json +++ b/plugins/PagerDuty/v2/dataStreams/teams.json @@ -2,7 +2,6 @@ "name": "teams", "displayName": "Teams (Import)", "description": "One row per PagerDuty team", - "tags": ["Teams"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", diff --git a/plugins/PagerDuty/v2/dataStreams/users.json b/plugins/PagerDuty/v2/dataStreams/users.json index 756bb590..cedc3e05 100644 --- a/plugins/PagerDuty/v2/dataStreams/users.json +++ b/plugins/PagerDuty/v2/dataStreams/users.json @@ -2,7 +2,6 @@ "name": "users", "displayName": "Users", "description": "PagerDuty user roster with role, contact and team associations, one row per user", - "tags": ["Users"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", @@ -24,7 +23,8 @@ { "type": "objects", "name": "user", - "label": "User (optional)", + "label": "User", + "placeholder": "Select...", "matches": { "sourceType": { "type": "oneOf", "values": ["PagerDuty User"] } } } ], diff --git a/plugins/PagerDuty/v2/dataStreams/validateAuth.json b/plugins/PagerDuty/v2/dataStreams/validateAuth.json index ad8930b1..77ce3261 100644 --- a/plugins/PagerDuty/v2/dataStreams/validateAuth.json +++ b/plugins/PagerDuty/v2/dataStreams/validateAuth.json @@ -2,7 +2,6 @@ "name": "validateAuth", "displayName": "Authentication Validation", "description": "Returns the calling account's enabled ability flags", - "tags": ["Utility"], "baseDataSourceName": "httpRequestUnscoped", "config": { "httpMethod": "get", diff --git a/plugins/PagerDuty/v2/defaultContent/overview.dash.json b/plugins/PagerDuty/v2/defaultContent/overview.dash.json index c792cf73..95def6c4 100644 --- a/plugins/PagerDuty/v2/defaultContent/overview.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/overview.dash.json @@ -108,9 +108,34 @@ } }, { - "i": "9bd9299c-e2fc-4b21-8d17-34b63438ef43", + "i": "e2976967-d5d0-4893-bc17-8854f82d938d", "x": 0, "y": 2, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents minus auto-resolved", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + } + } + }, + { + "i": "9bd9299c-e2fc-4b21-8d17-34b63438ef43", + "x": 0, + "y": 4, "w": 2, "h": 3, "z": 0, @@ -147,7 +172,7 @@ { "i": "29f32962-a552-4874-b889-8634ee14596d", "x": 2, - "y": 2, + "y": 4, "w": 2, "h": 3, "z": 0, @@ -185,7 +210,7 @@ { "i": "ef478f65-dc08-47b7-b225-a726272fbe46", "x": 0, - "y": 5, + "y": 7, "w": 2, "h": 3, "z": 0, @@ -231,7 +256,7 @@ { "i": "f50ef53c-c8d9-4c24-9cb2-f38fca101cc0", "x": 2, - "y": 5, + "y": 7, "w": 2, "h": 3, "z": 0, @@ -263,7 +288,7 @@ { "i": "06921b74-c2e9-4b5e-a46f-386f9ac88157", "x": 0, - "y": 8, + "y": 10, "w": 4, "h": 3, "z": 0, @@ -293,31 +318,6 @@ } } } - }, - { - "i": "e2976967-d5d0-4893-bc17-8854f82d938d", - "x": 0, - "y": 11, - "w": 1, - "h": 2, - "z": 0, - "moved": false, - "static": false, - "config": { - "_type": "tile/data-stream", - "title": "Incidents minus auto-resolved", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "dataStream": { - "id": "{{dataStreams.[incidentMetrics]}}", - "name": "incidentMetrics", - "pluginConfigId": "{{configId}}" - }, - "visualisation": { - "type": "data-stream-scalar", - "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } - } - } } ] } diff --git a/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json index fb7eeae0..460d8e49 100644 --- a/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/servicePerspective.dash.json @@ -244,17 +244,17 @@ } }, { - "i": "06bb25db-4bf6-4be8-9ff4-d5c57b43e321", + "i": "f4599a51-5c19-439c-98eb-bb31eb7382f0", "x": 0, "y": 6, - "w": 4, - "h": 3, + "w": 1, + "h": 2, "z": 0, "moved": false, "static": false, "config": { "_type": "tile/data-stream", - "title": "Incident Volume", + "title": "Incidents minus auto-resolved", "description": "", "activePluginConfigIds": ["{{configId}}"], "dataStream": { @@ -262,7 +262,6 @@ "name": "incidentMetrics", "pluginConfigId": "{{configId}}", "dataSourceConfig": { - "aggregateUnit": "day", "service": { "variable": "{{variables.[PagerDuty Service]}}", "workspace": "{{workspaceId}}", @@ -271,34 +270,23 @@ } }, "visualisation": { - "type": "data-stream-line-graph", - "config": { - "data-stream-line-graph": { - "xAxisColumn": "range_start", - "yAxisColumn": ["total_incident_count"], - "seriesColumn": "none", - "showLegend": false, - "legendPosition": "bottom", - "yAxisLabel": "Incidents", - "showYAxisLabel": true, - "showTrendLine": false - } - } + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } } } }, { - "i": "f4599a51-5c19-439c-98eb-bb31eb7382f0", + "i": "06bb25db-4bf6-4be8-9ff4-d5c57b43e321", "x": 0, - "y": 9, - "w": 1, - "h": 2, + "y": 8, + "w": 4, + "h": 3, "z": 0, "moved": false, "static": false, "config": { "_type": "tile/data-stream", - "title": "Incidents minus auto-resolved", + "title": "Incident Volume", "description": "", "activePluginConfigIds": ["{{configId}}"], "dataStream": { @@ -306,6 +294,7 @@ "name": "incidentMetrics", "pluginConfigId": "{{configId}}", "dataSourceConfig": { + "aggregateUnit": "day", "service": { "variable": "{{variables.[PagerDuty Service]}}", "workspace": "{{workspaceId}}", @@ -314,8 +303,19 @@ } }, "visualisation": { - "type": "data-stream-scalar", - "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "range_start", + "yAxisColumn": ["total_incident_count"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Incidents", + "showYAxisLabel": true, + "showTrendLine": false + } + } } } } diff --git a/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json b/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json index 44ab6c9f..0a7d1597 100644 --- a/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json +++ b/plugins/PagerDuty/v2/defaultContent/teamPerspective.dash.json @@ -203,9 +203,41 @@ } }, { - "i": "39dca1d8-9e66-4a18-9791-088761bed050", + "i": "7e13b2c1-55b1-45a0-8488-4f32f8358bf7", "x": 0, "y": 6, + "w": 1, + "h": 2, + "z": 0, + "moved": false, + "static": false, + "config": { + "_type": "tile/data-stream", + "title": "Incidents minus auto-resolved", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[incidentMetrics]}}", + "name": "incidentMetrics", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "team": { + "variable": "{{variables.[PagerDuty Team]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Teams]}}" + } + } + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } + } + } + }, + { + "i": "39dca1d8-9e66-4a18-9791-088761bed050", + "x": 0, + "y": 8, "w": 4, "h": 3, "z": 0, @@ -253,38 +285,6 @@ } } } - }, - { - "i": "7e13b2c1-55b1-45a0-8488-4f32f8358bf7", - "x": 0, - "y": 9, - "w": 1, - "h": 2, - "z": 0, - "moved": false, - "static": false, - "config": { - "_type": "tile/data-stream", - "title": "Incidents minus auto-resolved", - "description": "", - "activePluginConfigIds": ["{{configId}}"], - "dataStream": { - "id": "{{dataStreams.[incidentMetrics]}}", - "name": "incidentMetrics", - "pluginConfigId": "{{configId}}", - "dataSourceConfig": { - "team": { - "variable": "{{variables.[PagerDuty Team]}}", - "workspace": "{{workspaceId}}", - "scope": "{{scopes.[Teams]}}" - } - } - }, - "visualisation": { - "type": "data-stream-scalar", - "config": { "data-stream-scalar": { "value": "manuallyResolvedCount", "comparisonColumn": "none" } } - } - } } ] } diff --git a/plugins/PagerDuty/v2/docs/README.md b/plugins/PagerDuty/v2/docs/README.md index cea4d163..992a8995 100644 --- a/plugins/PagerDuty/v2/docs/README.md +++ b/plugins/PagerDuty/v2/docs/README.md @@ -45,7 +45,7 @@ The out-of-the-box dashboards include an account-wide **Overview**, a dedicated - **Users** — role, contact and team associations, one row per user. - **Incidents** — filterable by status, urgency, service, team and assignee, one row per incident, including who last modified/resolved it. - **Incident Metrics** — account-wide aggregated incident analytics, optionally bucketed by day, week or month. -- **Incident Metrics by Service/Team/Escalation Policy** — the same analytics grouped by dimension, one row per group. +- **Incident Metrics (by Service / Team / Escalation Policy)** — the same analytics grouped by dimension, one row per group. - **Responder Metrics** — aggregated responder performance, one row per responder. - **On-Call** — who is on call now and over an upcoming look-ahead window, one row per shift. - **Notifications** — outbound notifications sent to responders, one row per notification. diff --git a/plugins/PagerDuty/v2/ui.json b/plugins/PagerDuty/v2/ui.json index 1e29ee6a..b1ad0426 100644 --- a/plugins/PagerDuty/v2/ui.json +++ b/plugins/PagerDuty/v2/ui.json @@ -3,15 +3,15 @@ "type": "password", "name": "apiToken", "label": "API token", - "help": "A PagerDuty REST API key. Create one in **My Profile → User Settings → API Access** for a personal token, or **Integrations → API Access Keys** in the PagerDuty console for a general-access token that survives your account being removed.", - "placeholder": "Enter your API Token", + "help": "A PagerDuty REST API key. Use a personal token, or a general-access token if you want it to survive your account being removed.", + "placeholder": "Enter an API token", "validation": { "required": true } }, { "type": "radio", "name": "region", "label": "Service region", - "help": "The PagerDuty service region your account is hosted in. Check **Account Settings** in the PagerDuty console if you're not sure.", + "help": "The PagerDuty service region your account is hosted in. Check your account settings if you're not sure.", "defaultValue": "us", "options": [ { "value": "us", "label": "Global/US (api.pagerduty.com)" }, From 68a3ec36ebd1c075e16196354f83041ab8c9ba0e Mon Sep 17 00:00:00 2001 From: vinbab Date: Wed, 9 Sep 2026 11:07:08 +0100 Subject: [PATCH 5/8] Add an explicit None option to optional switch filters Bucket by, Urgency (Incident Metrics, Incident Metrics by dimension, Responder Metrics) and Channel (Notifications) now list "None" as the first, default-selected option instead of relying on toggling a switch to an unselected state. Live-tested: "None" resolves to no filter, same as before; real values still filter correctly. Co-Authored-By: Claude Sonnet 5 --- plugins/PagerDuty/v2/dataStreams/incidentMetrics.json | 10 +++++++--- .../v2/dataStreams/incidentMetricsBreakdown.json | 4 +++- plugins/PagerDuty/v2/dataStreams/notifications.json | 4 +++- plugins/PagerDuty/v2/dataStreams/responderMetrics.json | 4 +++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json index 7773e081..64e17486 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json @@ -10,12 +10,12 @@ "filters": { "created_at_start": "{{timeframe.start}}", "created_at_end": "{{timeframe.end}}", - "urgency": "{{urgency || null}}", + "urgency": "{{urgency && urgency !== 'none' ? urgency : null}}", "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", "service_ids": "{{service?.flatMap(o => o.rawId) || null}}" }, "time_zone": "{{dataSource.timezone}}", - "aggregate_unit": "{{aggregateUnit || null}}" + "aggregate_unit": "{{aggregateUnit && aggregateUnit !== 'none' ? aggregateUnit : null}}" }, "pathToData": "data" }, @@ -25,8 +25,10 @@ "type": "switch", "name": "aggregateUnit", "label": "Bucket by", - "help": "Leave unset for a single account-wide summary row over the selected timeframe.", + "help": "Leave as None for a single account-wide summary row over the selected timeframe.", + "defaultValue": "none", "options": [ + { "value": "none", "label": "None" }, { "value": "day", "label": "Day" }, { "value": "week", "label": "Week" }, { "value": "month", "label": "Month" } @@ -36,7 +38,9 @@ "type": "switch", "name": "urgency", "label": "Urgency", + "defaultValue": "none", "options": [ + { "value": "none", "label": "None" }, { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } ] diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json index bab8fdb9..b5a8962a 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json @@ -10,7 +10,7 @@ "filters": { "created_at_start": "{{timeframe.start}}", "created_at_end": "{{timeframe.end}}", - "urgency": "{{urgency || null}}", + "urgency": "{{urgency && urgency !== 'none' ? urgency : null}}", "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", "service_ids": "{{service?.flatMap(o => o.rawId) || null}}" }, @@ -36,7 +36,9 @@ "type": "switch", "name": "urgency", "label": "Urgency", + "defaultValue": "none", "options": [ + { "value": "none", "label": "None" }, { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } ] diff --git a/plugins/PagerDuty/v2/dataStreams/notifications.json b/plugins/PagerDuty/v2/dataStreams/notifications.json index ffc2ec99..bc8f1fdc 100644 --- a/plugins/PagerDuty/v2/dataStreams/notifications.json +++ b/plugins/PagerDuty/v2/dataStreams/notifications.json @@ -9,7 +9,7 @@ "getArgs": [ { "key": "since", "value": "{{timeframe.start}}" }, { "key": "until", "value": "{{timeframe.end}}" }, - { "key": "filter", "value": "{{channel || null}}" } + { "key": "filter", "value": "{{channel && channel !== 'none' ? channel : null}}" } ], "postRequestScript": "notifications.js", "paging": { @@ -36,7 +36,9 @@ "type": "switch", "name": "channel", "label": "Channel", + "defaultValue": "none", "options": [ + { "value": "none", "label": "None" }, { "value": "sms_notification", "label": "SMS" }, { "value": "email_notification", "label": "Email" }, { "value": "phone_notification", "label": "Phone" }, diff --git a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json index f531875c..c73f9c76 100644 --- a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json @@ -10,7 +10,7 @@ "filters": { "date_range_start": "{{timeframe.start}}", "date_range_end": "{{timeframe.end}}", - "urgency": "{{urgency || null}}", + "urgency": "{{urgency && urgency !== 'none' ? urgency : null}}", "team_ids": "{{team?.flatMap(o => o.rawId) || null}}", "responder_ids": "{{user?.flatMap(o => o.rawId) || null}}" }, @@ -24,7 +24,9 @@ "type": "switch", "name": "urgency", "label": "Urgency", + "defaultValue": "none", "options": [ + { "value": "none", "label": "None" }, { "value": "high", "label": "High" }, { "value": "low", "label": "Low" } ] From 203a9564722d9cc0eec40c0fba832d3700a34499 Mon Sep 17 00:00:00 2001 From: vinbab Date: Wed, 9 Sep 2026 11:15:33 +0100 Subject: [PATCH 6/8] Show an Apply button on every data stream with a Parameters step Sets manualConfigApply on the eight streams that expose tile parameters, so config changes are applied on demand rather than re-querying PagerDuty on every keystroke or picker change. The four import-only streams have no Parameters step and are left alone. Co-Authored-By: Claude Sonnet 5 --- plugins/PagerDuty/v2/dataStreams/incidentMetrics.json | 1 + plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json | 1 + plugins/PagerDuty/v2/dataStreams/incidents.json | 1 + plugins/PagerDuty/v2/dataStreams/notifications.json | 1 + plugins/PagerDuty/v2/dataStreams/onCall.json | 1 + plugins/PagerDuty/v2/dataStreams/responderMetrics.json | 1 + plugins/PagerDuty/v2/dataStreams/services.json | 1 + plugins/PagerDuty/v2/dataStreams/users.json | 1 + 8 files changed, 8 insertions(+) diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json index 64e17486..14ae913c 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetrics.json @@ -20,6 +20,7 @@ "pathToData": "data" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "switch", diff --git a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json index b5a8962a..2dd861ce 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json +++ b/plugins/PagerDuty/v2/dataStreams/incidentMetricsBreakdown.json @@ -19,6 +19,7 @@ "pathToData": "data" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "switch", diff --git a/plugins/PagerDuty/v2/dataStreams/incidents.json b/plugins/PagerDuty/v2/dataStreams/incidents.json index a14201f6..48854c1a 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidents.json +++ b/plugins/PagerDuty/v2/dataStreams/incidents.json @@ -28,6 +28,7 @@ "postRequestScript": "incidents.js" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "autocomplete", diff --git a/plugins/PagerDuty/v2/dataStreams/notifications.json b/plugins/PagerDuty/v2/dataStreams/notifications.json index bc8f1fdc..9f9aabec 100644 --- a/plugins/PagerDuty/v2/dataStreams/notifications.json +++ b/plugins/PagerDuty/v2/dataStreams/notifications.json @@ -24,6 +24,7 @@ } }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "objects", diff --git a/plugins/PagerDuty/v2/dataStreams/onCall.json b/plugins/PagerDuty/v2/dataStreams/onCall.json index 797e2258..9f79ce62 100644 --- a/plugins/PagerDuty/v2/dataStreams/onCall.json +++ b/plugins/PagerDuty/v2/dataStreams/onCall.json @@ -27,6 +27,7 @@ } }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "number", diff --git a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json index c73f9c76..e535c326 100644 --- a/plugins/PagerDuty/v2/dataStreams/responderMetrics.json +++ b/plugins/PagerDuty/v2/dataStreams/responderMetrics.json @@ -19,6 +19,7 @@ "pathToData": "data" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "switch", diff --git a/plugins/PagerDuty/v2/dataStreams/services.json b/plugins/PagerDuty/v2/dataStreams/services.json index 36ab3eb8..47d629aa 100644 --- a/plugins/PagerDuty/v2/dataStreams/services.json +++ b/plugins/PagerDuty/v2/dataStreams/services.json @@ -19,6 +19,7 @@ "postRequestScript": "services.js" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "objects", diff --git a/plugins/PagerDuty/v2/dataStreams/users.json b/plugins/PagerDuty/v2/dataStreams/users.json index cedc3e05..0c6cae9a 100644 --- a/plugins/PagerDuty/v2/dataStreams/users.json +++ b/plugins/PagerDuty/v2/dataStreams/users.json @@ -19,6 +19,7 @@ "postRequestScript": "users.js" }, "matches": "none", + "manualConfigApply": true, "ui": [ { "type": "objects", From 3eb08c7d206b6f45b8f83c79223b8bfc723a068f Mon Sep 17 00:00:00 2001 From: vinbab Date: Wed, 9 Sep 2026 14:14:18 +0100 Subject: [PATCH 7/8] Turn off the thousands separator on the Incidents Number column An incident number is an identifier, not a quantity, so it read as "28,413". Keeping the number shape (so sorting stays numeric) and disabling the separator renders it as 28413. Co-Authored-By: Claude Sonnet 5 --- plugins/PagerDuty/v2/dataStreams/incidents.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/PagerDuty/v2/dataStreams/incidents.json b/plugins/PagerDuty/v2/dataStreams/incidents.json index 48854c1a..43052c41 100644 --- a/plugins/PagerDuty/v2/dataStreams/incidents.json +++ b/plugins/PagerDuty/v2/dataStreams/incidents.json @@ -87,7 +87,7 @@ ], "metadata": [ { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, - { "name": "incidentNumber", "displayName": "Number", "shape": "number" }, + { "name": "incidentNumber", "displayName": "Number", "shape": ["number", { "thousandsSeparator": false }] }, { "name": "title", "displayName": "Title", "shape": "string", "role": "label" }, { "name": "status", From 7f152b71b5434e8927d6b2e3f13d9e2162441666 Mon Sep 17 00:00:00 2001 From: vinbab Date: Wed, 9 Sep 2026 16:16:13 +0100 Subject: [PATCH 8/8] Drop PagerDuty portal navigation paths from the plugin README The tooltips were made portal-agnostic because menu paths rot when the vendor rearranges their UI; the README carried the same breadcrumbs and is also surfaced in-product when adding the plugin. Keeps which token to create and why, drops the exact menu route. Co-Authored-By: Claude Sonnet 5 --- plugins/PagerDuty/v2/docs/README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/plugins/PagerDuty/v2/docs/README.md b/plugins/PagerDuty/v2/docs/README.md index 992a8995..19583ae7 100644 --- a/plugins/PagerDuty/v2/docs/README.md +++ b/plugins/PagerDuty/v2/docs/README.md @@ -7,22 +7,21 @@ schedules and responder performance in SquaredUp, via the You will need a PagerDuty **REST API key**. 1. Sign in to your [PagerDuty account](https://app.pagerduty.com). -2. For a key tied to your own user, go to **My Profile → User Settings** and - click **Create API User Token**. For a key that keeps working if your - account is ever removed, go to **Integrations → API Access Keys** instead - and click **Create New API Key** (requires admin permissions) — choose - **Read-only** unless you have a reason to grant write access. +2. Create the key from your user profile settings for a token tied to your own + user, or from your account's API access settings for a general-access key + that keeps working if your account is ever removed (creating one of those + requires admin permissions). Choose **Read-only** unless you have a reason + to grant write access. 3. Copy the generated key and paste it into the **API token** field. -4. Choose the **Service region** that matches your account (check - **Account Settings** in PagerDuty if you're unsure — most accounts are - **Global/US**). +4. Choose the **Service region** that matches your account — check your account + settings in PagerDuty if you're unsure; most accounts are **Global/US**. ## Configuration fields | Field | What it is | Where to find it | Required | | ----- | ---------- | ----------------- | -------- | -| **API token** | Authenticates every request via the `Authorization: Token token=...` header. | PagerDuty → **My Profile → User Settings** (personal) or **Integrations → API Access Keys** (general access). | Yes | -| **Service region** | Selects the PagerDuty API host (`api.pagerduty.com` or `api.eu.pagerduty.com`) for your account. | PagerDuty → **Account Settings**. | Yes | +| **API token** | Authenticates every request via the `Authorization: Token token=...` header. | Your PagerDuty user profile settings (personal token) or account API access settings (general access). | Yes | +| **Service region** | Selects the PagerDuty API host (`api.pagerduty.com` or `api.eu.pagerduty.com`) for your account. | Your PagerDuty account settings. | Yes | | **Timezone** | IANA timezone used to align time-based filters and aggregated results. | — | No | On save, the plugin validates the token by fetching your account's enabled