Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ plugins/AutoTask/* @TimWheeler-SQUP
plugins/DattoRMM/* @TimWheeler-SQUP
plugins/DigiCert/* @shaswot77
plugins/Huntress/* @Deenk
plugins/IncidentIO/ @noorulkhan-n
plugins/FantasyPremierLeague/* @TimWheeler-SQUP
plugins/GoogleSheets/* @kieranlangton
plugins/MetOffice/* @blackgrouse
Expand Down
11 changes: 11 additions & 0 deletions plugins/IncidentIO/v1/configValidation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"steps": [
{
"displayName": "Authenticate",
"dataStream": { "name": "currentUser" },
"required": true,
"error": "Could not authenticate. Check your API key is valid and has not expired.",
"success": "Connected successfully."
}
]
}
23 changes: 23 additions & 0 deletions plugins/IncidentIO/v1/custom_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"name": "Schedule",
"sourceType": "Schedule",
"icon": "calendar-days",
"singular": "Schedule",
"plural": "Schedules"
},
{
"name": "Team",
"sourceType": "Team",
"icon": "people-group",
"singular": "Team",
"plural": "Teams"
},
{
"name": "User",
"sourceType": "User",
"icon": "user",
"singular": "User",
"plural": "Users"
}
]
32 changes: 32 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/alerts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "alerts",
"displayName": "Alerts",
"description": "All alerts ingested by alert sources, one row per alert",
"tags": ["Alerts"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/alerts",
"pathToData": "alerts",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "50" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": "none",
"metadata": [
{ "name": "id", "displayName": "ID", "shape": "string", "visible": false },
{ "name": "title", "displayName": "Title", "shape": "string", "role": "label" },
{ "name": "status", "displayName": "Status", "shape": "string" },
{ "name": "alert_source_id", "displayName": "Alert Source ID", "shape": "string" },
{ "name": "deduplication_key", "displayName": "Deduplication Key", "shape": "string" },
{ "name": "created_at", "displayName": "Created At", "shape": "date" },
{ "name": "updated_at", "displayName": "Updated At", "shape": "date" },
{ "name": "resolved_at", "displayName": "Resolved At", "shape": "date" },
{ "name": "source_url", "displayName": "Source URL", "shape": "url" },
{ "pattern": ".*" }
],
"timeframes": false
}
15 changes: 15 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/currentUser.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "currentUser",
"displayName": "Current User",
"description": "Identity and role details for the configured API key",
"tags": ["Users"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"httpMethod": "get",
"endpointPath": "v1/identity"
},
"matches": "none",
"visibility": { "type": "hidden" },
"metadata": [{ "pattern": ".*" }],
"timeframes": false
}
36 changes: 36 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/incidents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "incidents",
"displayName": "Incidents",
"description": "All incidents in the organization, one row per incident",
"tags": ["Incidents"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/incidents",
"expandInnerObjects": true,
"pathToData": "incidents",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": "none",
"metadata": [
{ "name": "id", "displayName": "ID", "shape": "string", "visible": false },
{ "name": "name", "displayName": "Name", "shape": "string", "role": "label" },
{ "name": "reference", "displayName": "Reference", "shape": "string" },
{ "name": "mode", "displayName": "Mode", "shape": "string" },
{ "name": "visibility", "displayName": "Visibility", "shape": "string" },
{ "name": "incident_status.name", "displayName": "Status", "shape": "string" },
{ "name": "incident_status.category", "displayName": "Status Category", "shape": "string" },
{ "name": "severity.name", "displayName": "Severity", "shape": "string" },
{ "name": "incident_type.name", "displayName": "Type", "shape": "string" },
{ "name": "created_at", "displayName": "Created At", "shape": "date" },
{ "name": "updated_at", "displayName": "Updated At", "shape": "date" },
{ "name": "permalink", "displayName": "Permalink", "shape": "url" },
{ "pattern": ".*" }
],
"timeframes": false
}
26 changes: 26 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/scheduleOnCall.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "scheduleOnCall",
"displayName": "Schedule On-Call",
"description": "Who is currently on-call for a schedule, one row per active shift/layer",
"tags": ["On-Call", "Schedules"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "v2/schedules/{{object.rawId}}",
"pathToData": "schedule.current_shifts",
"expandInnerObjects": true
},
"matches": { "sourceType": { "type": "oneOf", "values": ["Schedule"] } },
"metadata": [
{ "name": "user.name", "displayName": "On-Call User", "shape": "string", "role": "label" },
{ "name": "user.email", "displayName": "Email", "shape": "string" },
{ "name": "start_at", "displayName": "Start At", "shape": "date", "role": "timestamp" },
{ "name": "end_at", "displayName": "End At", "shape": "date" },
{ "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" },
{ "name": "layer_id", "displayName": "Layer ID", "shape": "string" },
{ "name": "entry_id", "displayName": "Entry ID", "shape": "string", "visible": false },
{ "name": "fingerprint", "displayName": "Fingerprint", "shape": "string", "visible": false },
{ "pattern": ".*" }
],
"timeframes": false
}
32 changes: 32 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/scheduleShifts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "scheduleShifts",
"displayName": "Schedule Shifts",
"description": "The schedule's effective shifts across a time window, accounting for overrides",
"tags": ["On-Call", "Schedules"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "v2/schedule_entries",
"getArgs": [
{ "key": "schedule_id", "value": "{{object.rawId}}" },
{ "key": "entry_window_start", "value": "{{timeframe.start}}" },
{ "key": "entry_window_end", "value": "{{timeframe.end}}" }
],
"expandInnerObjects": true,
"pathToData": "schedule_entries.final",
"paging": { "mode": "none" }
},
"matches": { "sourceType": { "type": "oneOf", "values": ["Schedule"] } },
"metadata": [
{ "name": "user.name", "displayName": "On-Call User", "shape": "string", "role": "label" },
{ "name": "user.email", "displayName": "Email", "shape": "string" },
{ "name": "start_at", "displayName": "Start", "shape": "date", "role": "timestamp" },
{ "name": "end_at", "displayName": "End", "shape": "date" },
{ "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" },
{ "name": "layer_id", "displayName": "Layer ID", "shape": "string" },
{ "name": "entry_id", "displayName": "Entry ID", "shape": "string", "visible": false },
{ "name": "fingerprint", "displayName": "Fingerprint", "shape": "string", "visible": false },
{ "pattern": ".*" }
],
"timeframes": true
}
30 changes: 30 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/schedules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "schedules",
"displayName": "Schedules",
"description": "All on-call schedules in the organization, one row per schedule",
"tags": ["On-Call", "Schedules"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/schedules",
"pathToData": "schedules",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": "none",
"visibility": { "type": "hidden" },
"metadata": [
{ "name": "id", "displayName": "ID", "shape": "string", "visible": false },
{ "name": "name", "displayName": "Name", "shape": "string", "role": "label" },
{ "name": "timezone", "displayName": "Timezone", "shape": "string" },
{ "name": "created_at", "displayName": "Created At", "shape": "date" },
{ "name": "updated_at", "displayName": "Updated At", "shape": "date" },
Comment thread
clarkd marked this conversation as resolved.
{ "name": "permalink", "displayName": "Permalink", "shape": "url" },
{ "pattern": ".*" }
],
"timeframes": false
}
10 changes: 10 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/scripts/teams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// dataStreams/scripts/teams.js
// The v3/teams response nests a `members` array per team. A metadata
// valueExpression cannot compute a length from a sibling array/object
// column (cross-column references receive an already-shaped placeholder,
// not the real array), so the member count is derived here instead,
// against the untouched raw response.
result = (data.teams || []).map((team) => ({
...team,
memberCount: (team.members || []).length,
}));
30 changes: 30 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/scripts/userSchedules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// dataStreams/scripts/userSchedules.js
// Cross-references the schedules list against the scoped user's id: for each
// schedule, filters current_shifts to the ones where the shift's on-call
// user matches, and emits one row per matching (schedule, shift) pair. This
// can't be expressed with pathToData/valueExpression alone because it joins
// a nested per-schedule array against the scope object's id.
const userId = context.objects[0].rawId;

// This script only ever sees ONE page of `schedules` per invocation (`data` is that
// page's raw response body) β€” but the platform re-invokes it once per page as the
// `paging` config advances the `after` cursor, and concatenates every invocation's
// `result` into the stream's final output. Filtering per page and letting the
// platform concatenate is equivalent to concatenating all pages before filtering,
// since this is a stateless per-row filter with no cross-page aggregation β€” so every
// schedule, on every page, is checked. Confirmed empirically against this endpoint by
// forcing page_size=1 (2 pages for 2 real schedules) and seeing both schedules'
// shifts in the final result, each tagged with its own page's cursor.
result = (data.schedules || []).flatMap((schedule) =>
Comment thread
noorulkhan-n marked this conversation as resolved.
(schedule.current_shifts || [])
.filter((shift) => shift.user && shift.user.id === userId)
.map((shift) => ({
scheduleId: schedule.id,
scheduleName: schedule.name,
schedulePermalink: schedule.permalink,
start_at: shift.start_at,
end_at: shift.end_at,
rotation_id: shift.rotation_id || null,
layer_id: shift.layer_id || null,
})),
);
28 changes: 28 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/teams.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "teams",
"displayName": "Teams",
"description": "All teams in the organization, one row per team",
"tags": ["Teams"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v3/teams",
"postRequestScript": "teams.js",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": "none",
"visibility": { "type": "hidden" },
"metadata": [
{ "name": "id", "displayName": "ID", "shape": "string", "visible": false },
{ "name": "name", "displayName": "Name", "shape": "string", "role": "label" },
{ "name": "members", "displayName": "Members", "shape": "json", "visible": false },
{ "name": "memberCount", "displayName": "Member Count", "shape": "number" },
{ "pattern": ".*" }
],
"timeframes": false
}
29 changes: 29 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/userSchedules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "userSchedules",
"displayName": "User Schedules",
"description": "Schedules the user currently appears in as on-call, one row per matching shift",
"tags": ["On-Call", "Users"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "v2/schedules",
"postRequestScript": "userSchedules.js",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": { "sourceType": { "type": "oneOf", "values": ["User"] } },
"metadata": [
{ "name": "scheduleId", "displayName": "Schedule ID", "shape": "string", "visible": false },
{ "name": "scheduleName", "displayName": "Schedule", "shape": "string", "role": "label" },
{ "name": "schedulePermalink", "displayName": "Schedule Link", "shape": "url" },
{ "name": "start_at", "displayName": "Start", "shape": "date", "role": "timestamp" },
{ "name": "end_at", "displayName": "End", "shape": "date" },
{ "name": "rotation_id", "displayName": "Rotation ID", "shape": "string" },
{ "name": "layer_id", "displayName": "Layer ID", "shape": "string" }
],
"timeframes": false
}
33 changes: 33 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/users.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "users",
"displayName": "Users",
"description": "All users in the organization, one row per user",
"tags": ["Users"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/users",
"expandInnerObjects": true,
"pathToData": "users",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" },
"in": { "realm": "payload", "path": "pagination_meta.after" },
"out": { "realm": "queryArg", "path": "after" }
}
},
"matches": "none",
"visibility": { "type": "hidden" },
"metadata": [
{ "name": "id", "displayName": "ID", "shape": "string", "visible": false },
{ "name": "name", "displayName": "Name", "shape": "string", "role": "label" },
{ "name": "email", "displayName": "Email", "shape": "string" },
{ "name": "role", "displayName": "Role", "shape": "string" },
{ "name": "is_active", "displayName": "Active", "shape": "boolean" },
{ "name": "slack_user_id", "displayName": "Slack User ID", "shape": "string" },
{ "name": "seats.on_call", "displayName": "On-Call Seat", "shape": "string" },
{ "name": "seats.response", "displayName": "Response Seat", "shape": "string" },
{ "pattern": ".*" }
],
"timeframes": false
}
8 changes: 8 additions & 0 deletions plugins/IncidentIO/v1/defaultContent/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"items": [
{ "name": "overview", "type": "dashboard" },
{ "name": "schedulePerspective", "type": "dashboard" },
{ "name": "teamPerspective", "type": "dashboard" },
{ "name": "userPerspective", "type": "dashboard" }
]
}
Loading
Loading