Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
}
57 changes: 57 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/incidents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "incidents",
"displayName": "Incidents",
"description": "All incidents in the organization within the selected timeframe, one row per incident",
"tags": ["Incidents"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/incidents",
"expandInnerObjects": true,
"getArgs": [
{
"key": "created_at[date_range]",
"value": "{{timeframe.enum === 'none' ? null : (timeframe.start + '~' + timeframe.end)}}"
Comment thread
clarkd marked this conversation as resolved.
}
],
"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": [
"last1hour",
"last12hours",
"last24hours",
"last7days",
"last30days",
"thisMonth",
"thisQuarter",
"thisYear",
"lastMonth",
"lastQuarter",
"lastYear",
"none"
],
"supportsNoneTimeframe": true,
"defaultTimeframe": "none"
}
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
}
32 changes: 32 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/schedules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"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",
"postRequestScript": "schedules.js",
Comment thread
clarkd marked this conversation as resolved.
"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": "rotationCount", "displayName": "Rotations", "shape": "number" },
{ "name": "members", "displayName": "Members", "shape": "string" },
{ "name": "memberCount", "displayName": "Member Count", "shape": "number", "visible": false },
{ "name": "created_at", "displayName": "Created At", "shape": "date" },
{ "name": "updated_at", "displayName": "Updated At", "shape": "date" },
Comment thread
clarkd marked this conversation as resolved.
{ "name": "permalink", "displayName": "Permalink", "shape": "url" }
],
"timeframes": false
}
23 changes: 23 additions & 0 deletions plugins/IncidentIO/v1/dataStreams/scripts/schedules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// dataStreams/scripts/schedules.js
// The raw response nests rotation membership two levels deep
// (schedule.config.rotations[].users[]), which pathToData/valueExpression can't
// reach - flatten it into a readable, deduplicated list of member names.
result = (data.schedules || []).map((schedule) => {
const rotations = (schedule.config && schedule.config.rotations) || [];
const members = new Map();
rotations.forEach((rotation) =>
(rotation.users || []).forEach((user) => members.set(user.id, user.name)),
);

return {
id: schedule.id,
name: schedule.name,
timezone: schedule.timezone,
rotationCount: rotations.length,
members: [...members.values()].join(", "),
memberCount: members.size,
created_at: schedule.created_at,
updated_at: schedule.updated_at,
permalink: schedule.permalink,
};
});
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
}
Loading
Loading