-
Notifications
You must be signed in to change notification settings - Fork 7
Add incident.io plugin #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c28a645
5943a91
39d9333
9ed8522
284b4c6
80182d1
bdca994
1b445ac
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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." | ||
| } | ||
| ] | ||
| } |
| 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" | ||
| } | ||
| ] |
| 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 | ||
| } |
| 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": { | ||
| "httpMethod": "get", | ||
| "endpointPath": "v1/identity" | ||
| }, | ||
| "matches": "none", | ||
| "visibility": { "type": "hidden" }, | ||
| "metadata": [{ "pattern": ".*" }], | ||
| "timeframes": false | ||
| } | ||
| 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)}}" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π Maintainability & Code Quality | π΅ Trivial | β‘ Quick win Move the timeframe transformation into This line performs a conditional transformation inside a mustache-style value expression. Move the As per coding guidelines, data streams should prefer π€ Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
| ], | ||
| "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" | ||
| } | ||
| 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 | ||
| } |
| 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 | ||
| } |
| 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", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π Maintainability & Code Quality | π‘ Minor | β‘ Quick win π Supported by static analysisπ€ get_repo_knowledge executed:
Length of output: 22080 π Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- changed files ---'
git diff --name-status
printf '%s\n' '--- metadata ---'
if [ -f plugins/IncidentIO/v1/metadata.json ]; then
cat -n plugins/IncidentIO/v1/metadata.json
else
printf '%s\n' 'metadata.json not found'
fi
printf '%s\n' '--- relevant schedules files ---'
for f in plugins/IncidentIO/v1/dataStreams/schedules.json plugins/IncidentIO/v1/dataStreams/scripts/schedules.js; do
if [ -f "$f" ]; then
printf '\n--- %s ---\n' "$f"
cat -n "$f"
else
printf '%s not found\n' "$f"
fi
done
printf '%s\n' '--- metadata references ---'
rg -n '"version"|schedules\.js|schedules' plugins/IncidentIO/v1/metadata.json plugins/IncidentIO/v1/dataStreams/schedules.json plugins/IncidentIO/v1/dataStreams/scripts/schedules.js 2>/dev/null || trueRepository: squaredup/plugins Length of output: 5514 π Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- worktree ---'
git status --short
printf '%s\n' '--- latest commit ---'
git log -1 --oneline
printf '%s\n' '--- latest commit summary ---'
git diff --stat HEAD^ HEAD
printf '%s\n' '--- IncidentIO metadata history in latest commit ---'
git diff HEAD^ HEAD -- plugins/IncidentIO/v1/metadata.json
printf '%s\n' '--- IncidentIO schedules changes in latest commit ---'
git diff HEAD^ HEAD -- plugins/IncidentIO/v1/dataStreams/schedules.json plugins/IncidentIO/v1/dataStreams/scripts/schedules.js
printf '%s\n' '--- parent metadata, if present ---'
if git cat-file -e HEAD^:plugins/IncidentIO/v1/metadata.json 2>/dev/null; then
git show HEAD^:plugins/IncidentIO/v1/metadata.json | sed -n '1,12p'
else
printf '%s\n' 'metadata absent from parent'
fiRepository: squaredup/plugins Length of output: 3892 Increase the IncidentIO plugin version. This commit changes π€ Prompt for AI AgentsSource: Coding guidelines |
||
| "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 on lines
+27
to
+28
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π Maintainability & Code Quality | π‘ Minor | β‘ Quick win Use noun-based timestamp labels. Rename As per coding guidelines, data-stream display names should be noun-based. Proposed label fix- { "name": "created_at", "displayName": "Created At", "shape": "date" },
- { "name": "updated_at", "displayName": "Updated At", "shape": "date" },
+ { "name": "created_at", "displayName": "Creation Time", "shape": "date" },
+ { "name": "updated_at", "displayName": "Update Time", "shape": "date" },π€ Prompt for AI AgentsSource: Coding guidelines |
||
| { "name": "permalink", "displayName": "Permalink", "shape": "url" } | ||
| ], | ||
| "timeframes": false | ||
| } | ||
| 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, | ||
| }; | ||
| }); |
| 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, | ||
| })); |
| 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) => | ||
|
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, | ||
| })), | ||
| ); | ||
| 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 | ||
| } |
| 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 | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.