From 61823d28a903c15389b7cd87980ee36d6cfedd00 Mon Sep 17 00:00:00 2001 From: Tim Wheeler Date: Fri, 16 Oct 2026 09:40:04 +0100 Subject: [PATCH 1/3] Track CodeRabbit seats, reviews and security Adds a CodeRabbit data source so teams paying for CodeRabbit can see whether it is being used and whether its reviews help. It covers seat usage, pull request review times, how often CodeRabbit findings are accepted, AI Deep Scan security findings, MCP usage and the admin audit log, with dashboards for an overview, licenses, PRs and reviews, security, and a per-user perspective. Column derivations live in JSON value expressions where the platform allows it. Scripts remain only for what JSON cannot express: turning nested findings and MCP servers into rows, trimming the day-granular API window to an exact 24 hours, filtering security findings to several repositories when the API accepts only one, and deriving the PR number, because a url shape's label cannot read computed columns. Page sizes are deliberately mixed. The metrics endpoints use 1000, CodeRabbit's default, because at 100 per page paging stopped after one or two pages without an error and dashboards undercounted. Review comments uses 250 because each pull request carries its findings and a page of 1000 exceeded the 6MB response limit over a quarter. Co-Authored-By: Claude Opus 5.5 --- plugins/CodeRabbit/v1/configValidation.json | 18 + plugins/CodeRabbit/v1/custom_types.json | 23 + .../CodeRabbit/v1/dataStreams/auditLogs.json | 51 ++ .../CodeRabbit/v1/dataStreams/licenses.json | 37 + .../CodeRabbit/v1/dataStreams/mcpUsage.json | 66 ++ .../v1/dataStreams/organizations.json | 29 + .../v1/dataStreams/pullRequests.json | 292 ++++++ .../v1/dataStreams/repositories.json | 32 + .../v1/dataStreams/reviewComments.json | 80 ++ .../scripts/errorHandling/mcpUsage.js | 7 + .../scripts/errorHandling/securityFindings.js | 7 + .../v1/dataStreams/scripts/mcpUsage.js | 18 + .../v1/dataStreams/scripts/pullRequests.js | 29 + .../v1/dataStreams/scripts/repositories.js | 7 + .../v1/dataStreams/scripts/reviewComments.js | 25 + .../dataStreams/scripts/securityFindings.js | 8 + .../v1/dataStreams/securityFindings.json | 83 ++ plugins/CodeRabbit/v1/dataStreams/users.json | 30 + .../v1/defaultContent/licenses.dash.json | 559 ++++++++++++ .../v1/defaultContent/manifest.json | 24 + .../v1/defaultContent/overview.dash.json | 530 +++++++++++ .../v1/defaultContent/prsAndReviews.dash.json | 833 ++++++++++++++++++ .../CodeRabbit/v1/defaultContent/scopes.json | 53 ++ .../v1/defaultContent/security.dash.json | 562 ++++++++++++ .../v1/defaultContent/user.dash.json | 609 +++++++++++++ plugins/CodeRabbit/v1/docs/README.md | 68 ++ plugins/CodeRabbit/v1/icon.svg | 1 + .../v1/indexDefinitions/default.json | 50 ++ plugins/CodeRabbit/v1/metadata.json | 50 ++ plugins/CodeRabbit/v1/ui.json | 10 + 30 files changed, 4191 insertions(+) create mode 100644 plugins/CodeRabbit/v1/configValidation.json create mode 100644 plugins/CodeRabbit/v1/custom_types.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/auditLogs.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/licenses.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/mcpUsage.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/organizations.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/pullRequests.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/repositories.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/reviewComments.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/mcpUsage.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/securityFindings.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/mcpUsage.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/pullRequests.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/repositories.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/reviewComments.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/scripts/securityFindings.js create mode 100644 plugins/CodeRabbit/v1/dataStreams/securityFindings.json create mode 100644 plugins/CodeRabbit/v1/dataStreams/users.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/licenses.dash.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/manifest.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/overview.dash.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/prsAndReviews.dash.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/scopes.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/security.dash.json create mode 100644 plugins/CodeRabbit/v1/defaultContent/user.dash.json create mode 100644 plugins/CodeRabbit/v1/docs/README.md create mode 100644 plugins/CodeRabbit/v1/icon.svg create mode 100644 plugins/CodeRabbit/v1/indexDefinitions/default.json create mode 100644 plugins/CodeRabbit/v1/metadata.json create mode 100644 plugins/CodeRabbit/v1/ui.json diff --git a/plugins/CodeRabbit/v1/configValidation.json b/plugins/CodeRabbit/v1/configValidation.json new file mode 100644 index 00000000..65ffebeb --- /dev/null +++ b/plugins/CodeRabbit/v1/configValidation.json @@ -0,0 +1,18 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "organizations" }, + "required": true, + "success": "Connected to CodeRabbit.", + "error": "Could not connect. Check the API key is valid and the organization is on the CodeRabbit Enterprise plan." + }, + { + "displayName": "Check seat access", + "dataStream": { "name": "licenses" }, + "required": false, + "success": "Seat and license data is accessible.", + "error": "Seat and license data is unavailable. Create the API key as an organization admin to see seat usage." + } + ] +} diff --git a/plugins/CodeRabbit/v1/custom_types.json b/plugins/CodeRabbit/v1/custom_types.json new file mode 100644 index 00000000..a47ce8e2 --- /dev/null +++ b/plugins/CodeRabbit/v1/custom_types.json @@ -0,0 +1,23 @@ +[ + { + "name": "Organization", + "sourceType": "Organization", + "icon": "building", + "singular": "Organization", + "plural": "Organizations" + }, + { + "name": "Repository", + "sourceType": "Repository", + "icon": "code-branch", + "singular": "Repository", + "plural": "Repositories" + }, + { + "name": "User", + "sourceType": "User", + "icon": "user", + "singular": "User", + "plural": "Users" + } +] diff --git a/plugins/CodeRabbit/v1/dataStreams/auditLogs.json b/plugins/CodeRabbit/v1/dataStreams/auditLogs.json new file mode 100644 index 00000000..978f1f09 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/auditLogs.json @@ -0,0 +1,51 @@ +{ + "name": "auditLogs", + "displayName": "Audit logs", + "description": "Organization audit log entries for administrative actions", + "tags": ["Activity", "Security"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/audit-logs", + "pathToData": "data", + "expandInnerObjects": true, + "getArgs": [ + { "key": "date_from", "value": "{{timeframe.start}}" }, + { "key": "date_to", "value": "{{timeframe.end}}" }, + { "key": "search", "value": "{{search || null}}" } + ], + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "page_size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data" }, + "base": 1 + }, + "out": { "realm": "queryArg", "path": "page" } + }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" } + }, + "matches": "none", + "ui": [ + { + "type": "text", + "name": "search", + "label": "Search", + "placeholder": "Enter text to search" + } + ], + "metadata": [ + { "name": "createdAt", "displayName": "Time", "shape": "date", "role": "timestamp" }, + { "name": "actionLabel", "displayName": "Action", "shape": "string", "role": "label" }, + { "name": "action", "displayName": "Action key", "shape": "string", "visible": false }, + { "name": "actor.name", "displayName": "Actor", "shape": "string" }, + { "name": "actor.subtitle", "displayName": "Actor role", "shape": "string" }, + { "name": "actor.isBot", "displayName": "Bot", "shape": "boolean", "visible": false }, + { "name": "resourceTypeLabel", "displayName": "Resource type", "shape": "string" }, + { "name": "resourceSummary", "displayName": "Resource", "shape": "string" }, + { "name": "ipAddress", "displayName": "IP address", "shape": "string" }, + { "name": "id", "displayName": "ID", "shape": "string", "role": "id", "visible": false } + ], + "timeframes": true +} diff --git a/plugins/CodeRabbit/v1/dataStreams/licenses.json b/plugins/CodeRabbit/v1/dataStreams/licenses.json new file mode 100644 index 00000000..b7ff5f1d --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/licenses.json @@ -0,0 +1,37 @@ +{ + "name": "licenses", + "displayName": "Licenses", + "description": "Seats purchased, seats assigned and the seat assignment mode", + "tags": ["License"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/users", + "getArgs": [{ "key": "limit", "value": "1" }], + "paging": { "mode": "none" }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" } + }, + "matches": "none", + "metadata": [ + { "name": "seats_purchased", "displayName": "Seats purchased", "shape": "number" }, + { "name": "seats_assigned", "displayName": "Seats assigned", "shape": "number", "role": "value" }, + { + "name": "seatsAvailable", + "displayName": "Seats available", + "computed": true, + "valueExpression": "{{ Math.max($['seats_purchased'] - $['seats_assigned'], 0) }}", + "shape": "number" + }, + { + "name": "seatUtilization", + "displayName": "Seat utilization", + "computed": true, + "valueExpression": "{{ $['seats_purchased'] > 0 ? $['seats_assigned'] / $['seats_purchased'] * 100 : null }}", + "shape": ["percent", { "decimalPlaces": 1 }] + }, + { "name": "seat_assignment_mode", "displayName": "Seat assignment mode", "shape": "string" } + ], + "timeframes": ["none"], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" +} diff --git a/plugins/CodeRabbit/v1/dataStreams/mcpUsage.json b/plugins/CodeRabbit/v1/dataStreams/mcpUsage.json new file mode 100644 index 00000000..2331adb8 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/mcpUsage.json @@ -0,0 +1,66 @@ +{ + "name": "mcpUsage", + "displayName": "MCP usage", + "description": "Pull requests that used MCP servers during review, with insight and tool call counts per server", + "tags": ["Code Review", "Usage"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/metrics/mcp", + "pathToData": "data", + "getArgs": [ + { "key": "start_date", "value": "{{timeframe.start.slice(0, 10)}}" }, + { "key": "end_date", "value": "{{timeframe.end.slice(0, 10)}}" }, + { + "key": "repository_ids", + "value": "{{repository && repository.length ? repository.map(o => [].concat(o.rawId)[0]).join(',') : null}}" + }, + { + "key": "user_ids", + "value": "{{user && user.length ? user.map(o => [].concat(o.rawId)[0]).join(',') : null}}" + } + ], + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "1000" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "script", "script": "errorHandling/mcpUsage.js" }, + "postRequestScript": "mcpUsage.js" + }, + "matches": "none", + "ui": [ + { + "type": "objects", + "name": "repository", + "label": "Repository", + "matches": { "sourceType": { "type": "oneOf", "values": ["Repository"] } } + }, + { + "type": "objects", + "name": "user", + "label": "User", + "matches": { "sourceType": { "type": "oneOf", "values": ["User"] } } + } + ], + "metadata": [ + { "name": "prUrl", "displayName": "Pull request", "shape": ["url", { "label": "#{{column.prNumber}}" }], "role": "link" }, + { + "name": "prNumber", + "displayName": "PR number", + "shape": ["number", { "thousandsSeparator": false }], + "visible": false + }, + { "name": "merged_at", "displayName": "Merged", "shape": "date", "role": "timestamp" }, + { "name": "serverName", "displayName": "MCP server", "shape": "string", "role": "label" }, + { "name": "insightCount", "displayName": "Insights", "shape": "number" }, + { "name": "toolCallCount", "displayName": "Tool calls", "shape": "number" }, + { "name": "tools", "displayName": "Tools", "shape": "string" }, + { "name": "repository_id", "displayName": "Repository ID", "shape": "string", "visible": false }, + { "name": "author_id", "displayName": "Author ID", "shape": "string", "visible": false }, + { "name": "repositoryName", "displayName": "Repository", "sourceId": "repository_id", "sourceType": "Repository", "objectPropertyPath": "name" }, + { "name": "authorName", "displayName": "Author", "sourceId": "author_id", "sourceType": "User", "objectPropertyPath": "name" } + ], + "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter"] +} diff --git a/plugins/CodeRabbit/v1/dataStreams/organizations.json b/plugins/CodeRabbit/v1/dataStreams/organizations.json new file mode 100644 index 00000000..7ab7b201 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/organizations.json @@ -0,0 +1,29 @@ +{ + "name": "organizations", + "displayName": "Organizations", + "description": "CodeRabbit organizations the API key can access", + "tags": ["Organization"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/organizations", + "pathToData": "organizations", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" } + }, + "matches": "none", + "metadata": [ + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "provider", "displayName": "Git provider", "shape": "string" }, + { "name": "provider_organization_id", "displayName": "Provider organization ID", "shape": "string", "role": "id" }, + { "name": "id", "displayName": "CodeRabbit organization ID", "shape": "string", "visible": false } + ], + "timeframes": ["none"], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" +} diff --git a/plugins/CodeRabbit/v1/dataStreams/pullRequests.json b/plugins/CodeRabbit/v1/dataStreams/pullRequests.json new file mode 100644 index 00000000..bb2f7744 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/pullRequests.json @@ -0,0 +1,292 @@ +{ + "name": "pullRequests", + "displayName": "Pull requests", + "description": "Merged or closed pull requests reviewed by CodeRabbit, with review timings and comment outcomes", + "tags": [ + "Pull Request", + "Code Review" + ], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/metrics/reviews", + "getArgs": [ + { + "key": "start_date", + "value": "{{timeframe.start.slice(0,10)}}" + }, + { + "key": "end_date", + "value": "{{timeframe.end.slice(0,10)}}" + }, + { + "key": "status", + "value": "{{status || null}}" + }, + { + "key": "repository_ids", + "value": "{{repository && repository.length ? repository.map(o => Array.isArray(o.rawId) ? o.rawId[0] : o.rawId).join(',') : null}}" + }, + { + "key": "user_ids", + "value": "{{user && user.length ? user.map(o => Array.isArray(o.rawId) ? o.rawId[0] : o.rawId).join(',') : null}}" + } + ], + "paging": { + "mode": "token", + "pageSize": { + "realm": "queryArg", + "path": "limit", + "value": "1000" + }, + "in": { + "realm": "payload", + "path": "next_cursor" + }, + "out": { + "realm": "queryArg", + "path": "cursor" + } + }, + "errorHandling": { + "type": "path", + "realm": "payload", + "path": "error.message" + }, + "postRequestScript": "pullRequests.js" + }, + "matches": "none", + "ui": [ + { + "type": "radio", + "name": "status", + "label": "Status", + "defaultValue": "merged", + "options": [ + { + "value": "merged", + "label": "Merged" + }, + { + "value": "closed", + "label": "Closed" + }, + { + "value": "all", + "label": "All" + } + ] + }, + { + "type": "objects", + "name": "repository", + "label": "Repository (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Repository" + ] + } + } + }, + { + "type": "objects", + "name": "user", + "label": "User (optional)", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "User" + ] + } + } + } + ], + "metadata": [ + { + "name": "prUrl", + "displayName": "Pull request", + "shape": [ + "url", + { + "label": "#{{column.prNumber}}" + } + ], + "role": "link" + }, + { + "name": "prNumber", + "displayName": "Number", + "shape": ["number", { "thousandsSeparator": false }], + "visible": false + }, + { + "name": "repository_name", + "displayName": "Repository", + "shape": "string", + "role": "label" + }, + { + "name": "organization_name", + "displayName": "Organization", + "shape": "string" + }, + { + "name": "author_username", + "displayName": "Author", + "shape": "string" + }, + { + "name": "status", + "displayName": "Status", + "shape": "string" + }, + { + "name": "created_at", + "displayName": "Created", + "shape": "date" + }, + { + "name": "ready_for_review_at", + "displayName": "Ready for review", + "shape": "date" + }, + { + "name": "first_human_review_at", + "displayName": "First human review", + "shape": "date" + }, + { + "name": "last_commit_at", + "displayName": "Last commit", + "shape": "date" + }, + { + "name": "merged_at", + "displayName": "Merged", + "shape": "date", + "role": "timestamp" + }, + { + "name": "hoursToFirstHumanReview", + "displayName": "Hours to first human review", + "computed": true, + "valueExpression": "{{ $['first_human_review_at'] ? Math.max((new Date($['first_human_review_at']) - new Date($['ready_for_review_at'] || $['created_at'])) / 3600000, 0) : null }}", + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ] + }, + { + "name": "hoursToMerge", + "displayName": "Hours to merge", + "computed": true, + "valueExpression": "{{ $['merged_at'] ? Math.max((new Date($['merged_at']) - new Date($['ready_for_review_at'] || $['created_at'])) / 3600000, 0) : null }}", + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ] + }, + { + "name": "hoursToLastCommit", + "displayName": "Hours to last commit", + "computed": true, + "valueExpression": "{{ $['last_commit_at'] ? Math.max((new Date($['last_commit_at']) - new Date($['ready_for_review_at'] || $['created_at'])) / 3600000, 0) : null }}", + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ] + }, + { + "name": "review_iterations", + "displayName": "Review iterations", + "shape": "number" + }, + { + "name": "estimated_complexity", + "displayName": "Estimated complexity", + "shape": "number" + }, + { + "name": "estimated_review_minutes", + "displayName": "Estimated review time", + "shape": "minutes" + }, + { + "name": "commentsPosted", + "displayName": "Comments posted", + "shape": "number" + }, + { + "name": "commentsAccepted", + "displayName": "Comments accepted", + "shape": "number" + }, + { + "name": "acceptanceRate", + "displayName": "Acceptance rate", + "computed": true, + "valueExpression": "{{ $['commentsPosted'] > 0 ? $['commentsAccepted'] / $['commentsPosted'] * 100 : null }}", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "name": "criticalPosted", + "displayName": "Critical comments", + "shape": "number" + }, + { + "name": "majorPosted", + "displayName": "Major comments", + "shape": "number" + }, + { + "name": "securityPosted", + "displayName": "Security comments", + "shape": "number" + }, + { + "name": "repository_id", + "displayName": "Repository ID", + "shape": "string", + "visible": false + }, + { + "name": "author_id", + "displayName": "Author ID", + "shape": "string", + "visible": false + }, + { + "sourceId": "repository_id", + "sourceType": "Repository", + "name": "repository_name" + }, + { + "sourceId": "author_id", + "sourceType": "User", + "name": "author_username" + } + ], + "timeframes": [ + "last24hours", + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter" + ] +} diff --git a/plugins/CodeRabbit/v1/dataStreams/repositories.json b/plugins/CodeRabbit/v1/dataStreams/repositories.json new file mode 100644 index 00000000..97891bc9 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/repositories.json @@ -0,0 +1,32 @@ +{ + "name": "repositories", + "displayName": "Repositories", + "description": "Repositories CodeRabbit records for an organization, with installation status", + "tags": ["Repository"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/organizations/{{object.codeRabbitId}}/repositories", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" }, + "postRequestScript": "repositories.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Organization"] } }, + "metadata": [ + { "name": "repository_name", "displayName": "Repository", "shape": "string", "role": "label" }, + { "name": "repository_id", "displayName": "Repository ID", "shape": "string", "role": "id", "visible": false }, + { "name": "private", "displayName": "Private", "shape": "boolean" }, + { "name": "is_installed", "displayName": "Installed", "shape": "boolean" }, + { "name": "group_path", "displayName": "Group path", "shape": "string" }, + { "name": "organizationName", "displayName": "Organization", "shape": "string" }, + { "name": "organizationId", "displayName": "Organization ID", "shape": "string", "visible": false } + ], + "timeframes": ["none"], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" +} diff --git a/plugins/CodeRabbit/v1/dataStreams/reviewComments.json b/plugins/CodeRabbit/v1/dataStreams/reviewComments.json new file mode 100644 index 00000000..92adeebf --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/reviewComments.json @@ -0,0 +1,80 @@ +{ + "name": "reviewComments", + "displayName": "Review comments", + "description": "Findings raised by CodeRabbit on merged pull requests, one row per finding", + "tags": ["Code Review", "Pull Request"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/metrics/review-comments", + "getArgs": [ + { "key": "start_date", "value": "{{timeframe.start.slice(0,10)}}" }, + { "key": "end_date", "value": "{{timeframe.end.slice(0,10)}}" }, + { "key": "repository_ids", "value": "{{repository && repository.length ? repository.map(o => Array.isArray(o.rawId) ? o.rawId[0] : o.rawId).join(',') : null}}" }, + { "key": "user_ids", "value": "{{user && user.length ? user.map(o => Array.isArray(o.rawId) ? o.rawId[0] : o.rawId).join(',') : null}}" } + ], + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "250" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" }, + "postRequestScript": "reviewComments.js" + }, + "matches": "none", + "ui": [ + { "type": "objects", "name": "repository", "label": "Repository (optional)", "matches": { "sourceType": { "type": "oneOf", "values": ["Repository"] } } }, + { "type": "objects", "name": "user", "label": "User (optional)", "matches": { "sourceType": { "type": "oneOf", "values": ["User"] } } } + ], + "metadata": [ + { "name": "commentUrl", "displayName": "Comment", "shape": ["url", { "label": "View" }], "role": "link" }, + { "name": "prUrl", "displayName": "Pull request", "shape": ["url", { "label": "#{{column.prNumber}}" }], "role": "link" }, + { + "name": "prNumber", + "displayName": "PR number", + "shape": ["number", { "thousandsSeparator": false }], + "visible": false + }, + { "name": "severityKey", "displayName": "Severity key", "shape": "string", "visible": false }, + { "name": "categoryKey", "displayName": "Category key", "shape": "string", "visible": false }, + { + "name": "severity", + "displayName": "Severity", + "computed": true, + "valueExpression": "{{ $['severityKey'] ? $['severityKey'].charAt(0).toUpperCase() + $['severityKey'].slice(1).toLowerCase() : 'Unclassified' }}", + "shape": "string" + }, + { + "name": "severityRank", + "displayName": "Severity rank", + "computed": true, + "valueExpression": "{{ ({ critical: 1, major: 2, minor: 3, trivial: 4, info: 5 })[String($['severityKey']).toLowerCase()] || 6 }}", + "shape": "number", + "visible": false + }, + { + "name": "category", + "displayName": "Category", + "computed": true, + "valueExpression": "{{ $['categoryKey'] ? $['categoryKey'].charAt(0).toUpperCase() + $['categoryKey'].slice(1).toLowerCase().replace(/_/g, ' ') : 'Unclassified' }}", + "shape": "string" + }, + { "name": "accepted", "displayName": "Accepted", "shape": "boolean" }, + { + "name": "acceptedCount", + "displayName": "Accepted count", + "computed": true, + "valueExpression": "{{ $['accepted'] === true ? 1 : 0 }}", + "shape": "number" + }, + { "name": "merged_at", "displayName": "Merged", "shape": "date", "role": "timestamp" }, + { "name": "repository_id", "displayName": "Repository ID", "shape": "string", "visible": false }, + { "name": "author_id", "displayName": "Author ID", "shape": "string", "visible": false }, + { "name": "repositoryName", "displayName": "Repository", "sourceId": "repository_id", "sourceType": "Repository", "objectPropertyPath": "name" }, + { "name": "authorName", "displayName": "Author", "sourceId": "author_id", "sourceType": "User", "objectPropertyPath": "name" }, + { "sourceId": "repository_id", "sourceType": "Repository", "name": "repositoryName" }, + { "sourceId": "author_id", "sourceType": "User", "name": "authorName" } + ], + "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter"] +} diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/mcpUsage.js b/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/mcpUsage.js new file mode 100644 index 00000000..bcaab0e0 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/mcpUsage.js @@ -0,0 +1,7 @@ +const apiMsg = data && (data.message || (data.error && data.error.message) || data.error); + +if (response.status === 403) { + result = "MCP usage metrics are not enabled for this CodeRabbit organization"; +} else { + result = apiMsg || "Request failed with HTTP " + response.status; +} diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/securityFindings.js b/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/securityFindings.js new file mode 100644 index 00000000..3a97bfdf --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/errorHandling/securityFindings.js @@ -0,0 +1,7 @@ +const apiMsg = data && (data.message || (data.error && (data.error.message || data.error))); + +if (response.status === 403) { + result = 'AI Deep Scan findings need an API key with the security read permission' + (apiMsg ? ' (' + apiMsg + ')' : ''); +} else { + result = apiMsg || ('Request failed with HTTP ' + response.status); +} diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/mcpUsage.js b/plugins/CodeRabbit/v1/dataStreams/scripts/mcpUsage.js new file mode 100644 index 00000000..cf8e987e --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/mcpUsage.js @@ -0,0 +1,18 @@ +// Flatten each pull request into one row per MCP server used. +result = (data.data || []).flatMap((pr) => + (pr.servers || []).map((server) => { + const tools = server.tools || []; + return { + prUrl: pr.pr_url, + // The url shape's label can't read computed columns, so the number is derived here + prNumber: Number(String(pr.pr_url || "").split("/").pop()) || null, + merged_at: pr.merged_at, + serverName: server.server_name, + insightCount: server.insight_count, + toolCallCount: tools.reduce((sum, t) => sum + (t.call_count || 0), 0), + tools: tools.map((t) => t.tool_name).join(", "), + repository_id: pr.repository_id, + author_id: pr.author_id, + }; + }), +); diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/pullRequests.js b/plugins/CodeRabbit/v1/dataStreams/scripts/pullRequests.js new file mode 100644 index 00000000..e9d3c13c --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/pullRequests.js @@ -0,0 +1,29 @@ +// Flatten nested comment counts and trim the day-granular API window to a 24 hour timeframe. +const start = new Date(context.timeframe.start); +const end = new Date(context.timeframe.end); +const trim = context.timeframe.enum === 'last24hours'; + +result = (data.data || []) + .filter((pr) => { + // Only merged PRs expose a finalized time; closed PRs stay at day granularity + if (!trim || !pr.merged_at) return true; + const t = new Date(pr.merged_at); + return t >= start && t <= end; + }) + .map(({ coderabbit_comments: comments = {}, ...pr }) => { + const total = comments.total || {}; + const severity = comments.severity || {}; + const category = comments.category || {}; + return { + ...pr, + prUrl: pr.pr_url, + // The url shape's label can't read computed columns, so the number is derived here + prNumber: Number(String(pr.pr_url || '').split('/').pop()) || null, + commentsPosted: total.posted ?? 0, + commentsAccepted: total.accepted ?? 0, + criticalPosted: severity.critical?.posted ?? 0, + majorPosted: severity.major?.posted ?? 0, + // The reviews endpoint returns upper-case category keys, unlike its docs + securityPosted: (category.SECURITY_AND_PRIVACY ?? category.security_and_privacy)?.posted ?? 0, + }; + }); diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/repositories.js b/plugins/CodeRabbit/v1/dataStreams/scripts/repositories.js new file mode 100644 index 00000000..0d52bb0f --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/repositories.js @@ -0,0 +1,7 @@ +// Tag each repository with its parent organization, which the API response omits. +const org = context.objects[0] || {}; +result = (data.repositories || []).map((repo) => ({ + ...repo, + organizationId: org.rawId, + organizationName: org.name, +})); diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/reviewComments.js b/plugins/CodeRabbit/v1/dataStreams/scripts/reviewComments.js new file mode 100644 index 00000000..c4300226 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/reviewComments.js @@ -0,0 +1,25 @@ +// Flatten the nested findings array so each finding is a row. +// last24hours is narrower than the date-only API filter, so trim by merge time +const start = Date.parse(context.timeframe.start); +const end = Date.parse(context.timeframe.end); +const inWindow = (mergedAt) => { + const t = Date.parse(mergedAt); + return Number.isNaN(t) || (t >= start && t <= end); +}; + +result = (data.data || []) + .filter((pr) => inWindow(pr.merged_at)) + .flatMap((pr) => + (pr.coderabbit_comments || []).map((c) => ({ + commentUrl: c.url, + prUrl: pr.pr_url, + // The url shape's label can't read computed columns, so the number is derived here + prNumber: Number(String(pr.pr_url || "").split("/").pop()) || null, + severityKey: c.severity, + categoryKey: c.category, + accepted: c.accepted === true, + merged_at: pr.merged_at, + repository_id: pr.repository_id, + author_id: pr.author_id, + })), + ); diff --git a/plugins/CodeRabbit/v1/dataStreams/scripts/securityFindings.js b/plugins/CodeRabbit/v1/dataStreams/scripts/securityFindings.js new file mode 100644 index 00000000..ebc31d01 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/scripts/securityFindings.js @@ -0,0 +1,8 @@ +// The API filters by a single repo_id only, so narrow to the selected repositories here when more +// than one is chosen. The bulky evidence fields are dropped to keep the response small. +const selected = (context.config && context.config.repository) || []; +const ids = new Set(selected.map((o) => [].concat(o.rawId)[0])); + +result = (data.data || []) + .filter((f) => ids.size === 0 || ids.has(f.repository_id)) + .map(({ reachability_call_stack, scan_provenance, ...f }) => f); diff --git a/plugins/CodeRabbit/v1/dataStreams/securityFindings.json b/plugins/CodeRabbit/v1/dataStreams/securityFindings.json new file mode 100644 index 00000000..6f5be7b6 --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/securityFindings.json @@ -0,0 +1,83 @@ +{ + "name": "securityFindings", + "displayName": "Security findings", + "description": "AI Deep Scan code findings with severity, CWE and location", + "tags": ["Security", "Repository"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/security/scans/code", + "pathToData": "data", + "getArgs": [ + { "key": "format", "value": "json" }, + { "key": "state", "value": "{{state || 'open'}}" }, + { + "key": "repo_id", + "value": "{{(typeof repository !== 'undefined' && repository && repository.length === 1) ? [].concat(repository[0].rawId)[0] : null}}" + } + ], + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "1000" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "script", "script": "errorHandling/securityFindings.js" }, + "postRequestScript": "securityFindings.js" + }, + "matches": "none", + "ui": [ + { + "type": "radio", + "name": "state", + "label": "State", + "defaultValue": "open", + "options": [ + { "value": "open", "label": "Open" }, + { "value": "fixed", "label": "Fixed" }, + { "value": "dismissed", "label": "Dismissed" }, + { "value": "all", "label": "All" } + ] + }, + { + "type": "objects", + "name": "repository", + "label": "Repository (optional)", + "matches": { "sourceType": { "type": "oneOf", "values": ["Repository"] } } + } + ], + "metadata": [ + { + "name": "summary", + "displayName": "Finding", + "computed": true, + "valueExpression": "{{ (() => { const s = String($['description'] || '').replace(/\\\\s+/g, ' ').trim().split(/(?<=[.!?])\\\\s/)[0]; return s.length > 140 ? s.slice(0, 137) + '...' : s; })() }}", + "shape": "string", + "role": "label" + }, + { "name": "id", "displayName": "Finding ID", "shape": "string", "role": "id", "visible": false }, + { "name": "severity", "displayName": "Severity", "shape": "string", "formatExpression": "{{ $['severity'] ? $['severity'].charAt(0).toUpperCase() + $['severity'].slice(1).toLowerCase() : '' }}" }, + { + "name": "severityRank", + "displayName": "Severity rank", + "computed": true, + "valueExpression": "{{ ({ critical: 5, high: 4, medium: 3, low: 2, info: 1 })[String($['severity']).toLowerCase()] || 0 }}", + "shape": "number", + "visible": false + }, + { "name": "state", "displayName": "State", "shape": "string", "formatExpression": "{{ $['state'] ? $['state'].charAt(0).toUpperCase() + $['state'].slice(1) : '' }}" }, + { "name": "cwe", "displayName": "CWE", "shape": "string" }, + { "name": "repository_name", "displayName": "Repository", "shape": "string" }, + { "name": "repository_id", "displayName": "Repository ID", "shape": "string", "visible": false }, + { "sourceId": "repository_id", "sourceType": "Repository", "name": "repository_name" }, + { "name": "file_path", "displayName": "File", "shape": "string" }, + { "name": "start_line", "displayName": "Start line", "shape": "number" }, + { "name": "end_line", "displayName": "End line", "shape": "number" }, + { "name": "description", "displayName": "Description", "shape": "string", "visible": false }, + { "name": "organization_name", "displayName": "Organization", "shape": "string" }, + { "name": "organization_id", "displayName": "Organization ID", "shape": "string", "visible": false } + ], + "timeframes": ["none"], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" +} diff --git a/plugins/CodeRabbit/v1/dataStreams/users.json b/plugins/CodeRabbit/v1/dataStreams/users.json new file mode 100644 index 00000000..7c2c0fba --- /dev/null +++ b/plugins/CodeRabbit/v1/dataStreams/users.json @@ -0,0 +1,30 @@ +{ + "name": "users", + "displayName": "Users", + "description": "CodeRabbit users with their seat assignment and role", + "tags": ["Users", "License"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/v1/users", + "pathToData": "users", + "paging": { + "mode": "token", + "pageSize": { "realm": "queryArg", "path": "limit", "value": "100" }, + "in": { "realm": "payload", "path": "next_cursor" }, + "out": { "realm": "queryArg", "path": "cursor" } + }, + "errorHandling": { "type": "path", "realm": "payload", "path": "error.message" } + }, + "matches": "none", + "metadata": [ + { "name": "username", "displayName": "Username", "shape": "string", "role": "label" }, + { "name": "user_id", "displayName": "User ID", "shape": "string", "role": "id", "visible": false }, + { "name": "seat_assigned", "displayName": "Seat assigned", "shape": "boolean" }, + { "name": "role_name", "displayName": "Role", "shape": "string" }, + { "sourceId": "user_id", "sourceType": "User", "name": "username" } + ], + "timeframes": ["none"], + "supportsNoneTimeframe": true, + "defaultTimeframe": "none" +} diff --git a/plugins/CodeRabbit/v1/defaultContent/licenses.dash.json b/plugins/CodeRabbit/v1/defaultContent/licenses.dash.json new file mode 100644 index 00000000..0b70e02a --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/licenses.dash.json @@ -0,0 +1,559 @@ +{ + "name": "Licenses", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "8367698f-156a-4fdf-bafd-e08a0832bfa1", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seats Purchased", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "seats_purchased", + "type": "quantitative", + "title": "Seats Purchased" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "daa3150a-3c3f-442b-88e3-1e23103a84ea", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seats Assigned", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "seats_assigned", + "type": "quantitative", + "title": "Seats Assigned" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "bf1dd624-8b9d-4fd6-bcc7-62700a0b5bd5", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seats Available", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "seatsAvailable", + "type": "quantitative", + "title": "Seats Available" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "8bed8a63-fba9-46e5-ac91-07861bbcb1bf", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seat Utilization", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "seatUtilization", + "type": "quantitative", + "title": "Seat Utilization" + } + }, + "style": { + "gaugeMin": 0, + "gaugeMax": 100, + "gaugeBands": [ + { + "to": 0.5, + "color": "#DE0038" + }, + { + "to": 0.99, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "a580c996-9227-4766-bfbc-0ded51000f62", + "x": 0, + "y": 2, + "w": 3, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Active Users Per Day", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + }, + "group": { + "by": [ + [ + "merged_at", + "byDay" + ] + ], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "author_id" + ] + } + ] + }, + "sort": { + "by": [ + [ + "merged_at_byDay", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "merged_at_byDay", + "type": "temporal", + "title": "Merged" + }, + "y": { + "field": "author_id_distinctCount", + "type": "quantitative", + "title": "Active Users" + } + }, + "style": { + "legendPosition": "none", + "cartesianAreaGradient": true + } + } + } + } + }, + { + "i": "b10c6103-9ea1-4625-a6a8-2a4381212022", + "x": 3, + "y": 2, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seat Assignment Mode", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "seat_assignment_mode", + "type": "nominal", + "title": "Seat Assignment Mode" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "99648d6f-5ae5-4264-a382-54d715f3047f", + "x": 0, + "y": 6, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Users", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[users]}}", + "name": "users", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "username", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "username", + "title": "Username", + "renderer": { + "type": "text" + } + }, + { + "field": "seat_assigned", + "title": "Seat Assigned", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + }, + "width": 140 + }, + { + "field": "role_name", + "title": "Role", + "renderer": { + "type": "text" + }, + "width": 160 + } + ] + }, + "style": { + "richTableSort": { + "field": "username", + "direction": "asc" + } + } + } + } + } + }, + { + "i": "6747238e-48ec-4cb6-9593-ecb397b9f06d", + "x": 2, + "y": 6, + "w": 2, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Admin Activity", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[auditLogs]}}", + "name": "auditLogs", + "pluginConfigId": "{{configId}}", + "sort": { + "by": [ + [ + "createdAt", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "createdAt", + "title": "Time", + "renderer": { + "type": "date", + "relative": true + }, + "width": 120 + }, + { + "field": "actionLabel", + "title": "Action", + "renderer": { + "type": "text" + } + }, + { + "field": "actor.name", + "title": "Actor", + "renderer": { + "type": "text" + } + }, + { + "field": "resourceTypeLabel", + "title": "Resource Type", + "renderer": { + "type": "text" + }, + "width": 130 + }, + { + "field": "resourceSummary", + "title": "Resource", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableSort": { + "field": "createdAt", + "direction": "desc" + } + } + } + } + } + }, + { + "i": "3f141345-2937-4a6c-add5-ccff36dae1ec", + "x": 0, + "y": 11, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Idle Seats", + "description": "Users holding a seat with no merged pull request in the timeframe.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT u.\"username\" AS \"username\", u.\"role_name\" AS \"role\"\nFROM \"dataset1\" u\nWHERE u.\"seat_assigned\" = TRUE\n AND CAST(u.\"user_id\" AS VARCHAR) NOT IN (\n SELECT DISTINCT CAST(\"author_id\" AS VARCHAR) FROM \"dataset2\" WHERE \"author_id\" IS NOT NULL\n )\nORDER BY u.\"username\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "timeframe": "none", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[users]}}", + "name": "users", + "pluginConfigId": "{{configId}}" + } + } + }, + { + "tableName": "dataset2", + "config": { + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + } + } + } + } + ] + }, + "metadata": [ + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "username", + "title": "Username", + "renderer": { + "type": "text" + } + }, + { + "field": "role", + "title": "Role", + "renderer": { + "type": "text" + }, + "width": 160 + } + ] + }, + "style": { + "richTableSort": { + "field": "username", + "direction": "asc" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/CodeRabbit/v1/defaultContent/manifest.json b/plugins/CodeRabbit/v1/defaultContent/manifest.json new file mode 100644 index 00000000..0a8da548 --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/manifest.json @@ -0,0 +1,24 @@ +{ + "items": [ + { + "name": "overview", + "type": "dashboard" + }, + { + "name": "licenses", + "type": "dashboard" + }, + { + "name": "prsAndReviews", + "type": "dashboard" + }, + { + "name": "security", + "type": "dashboard" + }, + { + "name": "user", + "type": "dashboard" + } + ] +} diff --git a/plugins/CodeRabbit/v1/defaultContent/overview.dash.json b/plugins/CodeRabbit/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..0bb9c2c7 --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/overview.dash.json @@ -0,0 +1,530 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "8f34d2cc-0962-4d85-8f8c-4ee90b7eb853", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Seats Used", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "dataStream": { + "id": "{{dataStreams.[licenses]}}", + "name": "licenses", + "pluginConfigId": "{{configId}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "seats_assigned", + "type": "quantitative", + "title": "Seats Used" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "a417d00b-16d1-419a-b202-5da9daa40db8", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Active Users", + "description": "Authors with at least one merged pull request.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "author_id" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "author_id_distinctCount", + "type": "quantitative", + "title": "Active Users" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "e844ce7e-97ea-4df3-9631-d9b9adb0fb1c", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "PRs Merged", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "PRs Merged" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "c8ffc787-8014-403d-b14f-ce822aad9c40", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate", + "description": "Share of CodeRabbit findings that were accepted.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\" FROM \"dataset1\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}" + } + } + } + ] + }, + "metadata": [ + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + } + }, + "style": { + "gaugeMin": 0, + "gaugeMax": 100, + "gaugeBands": [ + { + "to": 0.6, + "color": "#DE0038" + }, + { + "to": 0.8, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "3788ab33-9823-42b0-afa2-5debd33cc683", + "x": 0, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Estimated Review Hours", + "description": "Review effort CodeRabbit estimated for merged pull requests.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT SUM(\"estimated_review_minutes\") / 60.0 AS \"estimatedReviewHours\" FROM \"dataset1\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + } + } + } + } + ] + }, + "metadata": [ + { + "name": "estimatedReviewHours", + "shape": [ + "number", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "estimatedReviewHours", + "type": "quantitative", + "title": "Hours" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "aa0ec097-07f9-4e7c-9ba9-43cc4672eedf", + "x": 2, + "y": 2, + "w": 2, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Findings Posted", + "description": "CodeRabbit review findings on merged pull requests.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "Findings Posted" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "b2ac0510-47ad-4b90-9e1d-7e5ce560d666", + "x": 0, + "y": 4, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "PRs Merged Per Day", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged" + }, + "group": { + "by": [ + [ + "merged_at", + "byDay" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "merged_at_byDay", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "merged_at_byDay", + "type": "temporal", + "title": "Merged" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "PRs Merged" + } + }, + "style": { + "legendPosition": "none", + "colors": [ + "#FF570A" + ], + "cartesianColorRules": [ + { + "gte": 0, + "color": "#FF570A" + } + ] + } + } + } + } + }, + { + "i": "4dba54bd-de9b-4d44-ad70-2de31d70cdbd", + "x": 2, + "y": 4, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate Over Time", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT\n STRFTIME(DATE_TRUNC('day', \"merged_at\"), '%Y-%m-%dT%H:%M:%SZ') AS \"day\",\n AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\"\nFROM \"dataset1\"\nWHERE \"merged_at\" IS NOT NULL\nGROUP BY 1\nORDER BY 1", + "tables": [ + { + "tableName": "dataset1", + "config": { + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}" + } + } + } + ] + }, + "metadata": [ + { + "name": "day", + "shape": "date" + }, + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "day", + "type": "temporal", + "title": "Merged" + }, + "y": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + } + }, + "style": { + "legendPosition": "none", + "cartesianYAxisMin": 0, + "cartesianYAxisMax": 100, + "cartesianAreaGradient": true + } + } + } + } + } + ] + } +} diff --git a/plugins/CodeRabbit/v1/defaultContent/prsAndReviews.dash.json b/plugins/CodeRabbit/v1/defaultContent/prsAndReviews.dash.json new file mode 100644 index 00000000..6443f63d --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/prsAndReviews.dash.json @@ -0,0 +1,833 @@ +{ + "name": "PRs & Reviews", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[Repository]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "77b7900a-a918-428a-9ac6-e33b0b8470b6", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Median Time to First Human Review", + "description": "Hours from ready for review to the first human review.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "median", + "names": [ + "hoursToFirstHumanReview" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "hoursToFirstHumanReview_median", + "type": "quantitative", + "title": "Hours" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "0b7a2ae7-1333-41c7-8523-b7c472bf76b0", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Median Time to Merge", + "description": "Hours from ready for review to merge.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "median", + "names": [ + "hoursToMerge" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "hoursToMerge_median", + "type": "quantitative", + "title": "Hours" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "267b4bdd-4d22-49e4-b71f-8f496ebf7bbb", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate", + "description": "Share of CodeRabbit findings that were accepted.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\" FROM \"dataset1\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "variables": [ + "{{variables.[Repository]}}" + ], + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + } + } + } + } + ] + }, + "metadata": [ + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + } + }, + "style": { + "gaugeMin": 0, + "gaugeMax": 100, + "gaugeBands": [ + { + "to": 0.6, + "color": "#DE0038" + }, + { + "to": 0.8, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "f4f4c8ea-1460-4347-a046-35e6220b7984", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "PRs Merged", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "PRs Merged" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "33933337-ab10-49cc-8e53-b3f0e7f07061", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Time to Merge Trend", + "description": "Median hours from ready for review to merge, by merge day.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [ + [ + "merged_at", + "byDay" + ] + ], + "aggregate": [ + { + "type": "median", + "names": [ + "hoursToMerge" + ] + } + ] + }, + "sort": { + "by": [ + [ + "merged_at_byDay", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "line", + "encoding": { + "x": { + "field": "merged_at_byDay", + "type": "temporal", + "title": "Merged" + }, + "y": { + "field": "hoursToMerge_median", + "type": "quantitative", + "title": "Hours to Merge" + } + }, + "style": { + "legendPosition": "none", + "cartesianAreaGradient": true + } + } + } + } + }, + { + "i": "a907092f-30e7-499b-8399-ea2ae5b8ef82", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate by Severity", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT\n \"severity\",\n MIN(\"severityRank\") AS \"severityRank\",\n COUNT(*) AS \"findings\",\n AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\"\nFROM \"dataset1\"\nGROUP BY \"severity\"\nORDER BY \"severityRank\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "variables": [ + "{{variables.[Repository]}}" + ], + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + } + } + } + } + ] + }, + "metadata": [ + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "ringGrid", + "encoding": { + "value": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + }, + "label": { + "field": "severity", + "type": "nominal", + "title": "Severity" + } + }, + "style": { + "ringGridMax": 100, + "ringGridThresholds": [ + { + "to": 0.6, + "color": "#DE0038" + }, + { + "to": 0.8, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "87da594e-999f-422d-9fe0-8043c7728527", + "x": 0, + "y": 6, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate by Category", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT\n \"category\",\n MIN(\"severityRank\") AS \"severityRank\",\n COUNT(*) AS \"findings\",\n AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\"\nFROM \"dataset1\"\nGROUP BY \"category\"\nORDER BY \"acceptanceRate\" DESC", + "tables": [ + { + "tableName": "dataset1", + "config": { + "variables": [ + "{{variables.[Repository]}}" + ], + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + } + } + } + } + ] + }, + "metadata": [ + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "ringGrid", + "encoding": { + "value": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + }, + "label": { + "field": "category", + "type": "nominal", + "title": "Category" + } + }, + "style": { + "ringGridMax": 100, + "ringGridThresholds": [ + { + "to": 0.6, + "color": "#DE0038" + }, + { + "to": 0.8, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "83b0df73-2b8f-471f-9fd2-3176efbffdac", + "x": 2, + "y": 6, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Critical and Major Findings", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "filter": { + "multiOperation": "or", + "filters": [ + { + "column": "severity", + "operation": "equals", + "value": "Critical" + }, + { + "column": "severity", + "operation": "equals", + "value": "Major" + } + ] + }, + "sort": { + "by": [ + [ + "severityRank", + "asc" + ], + [ + "merged_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "severity", + "title": "Severity", + "renderer": { + "type": "badge", + "colors": { + "Critical": "#c01616", + "Major": "#f97316", + "Minor": "#f59e0b", + "Trivial": "#3b82f6", + "Info": "#94a3b8", + "Unclassified": "#cbd5e1" + } + }, + "width": 100 + }, + { + "field": "commentUrl", + "title": "Finding", + "renderer": { + "type": "link", + "labelField": "category" + } + }, + { + "field": "prUrl", + "title": "Pull Request", + "renderer": { + "type": "link", + "labelField": "prNumber" + }, + "width": 110 + }, + { + "field": "repositoryName", + "title": "Repository", + "renderer": { + "type": "text" + } + }, + { + "field": "accepted", + "title": "Accepted", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + }, + "width": 100 + } + ] + }, + "style": {} + } + } + } + }, + { + "i": "65b021ef-6fd3-4d78-88bd-069f5855c1c6", + "x": 0, + "y": 10, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Pull Requests", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "sort": { + "by": [ + [ + "merged_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "prUrl", + "title": "Pull Request", + "renderer": { + "type": "link", + "labelField": "prNumber" + }, + "width": 110 + }, + { + "field": "repository_name", + "title": "Repository", + "renderer": { + "type": "text" + } + }, + { + "field": "author_username", + "title": "Author", + "renderer": { + "type": "text" + } + }, + { + "field": "merged_at", + "title": "Merged", + "renderer": { + "type": "date" + }, + "width": 170 + }, + { + "field": "hoursToMerge", + "title": "Hours to Merge", + "renderer": { + "type": "number", + "decimals": 1 + }, + "align": "right", + "width": 130 + }, + { + "field": "review_iterations", + "title": "Review Iterations", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 140 + }, + { + "field": "commentsPosted", + "title": "Comments Posted", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 140 + }, + { + "field": "commentsAccepted", + "title": "Comments Accepted", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 150 + } + ] + }, + "style": { + "richTableSort": { + "field": "merged_at", + "direction": "desc" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/CodeRabbit/v1/defaultContent/scopes.json b/plugins/CodeRabbit/v1/defaultContent/scopes.json new file mode 100644 index 00000000..1a3d950f --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/scopes.json @@ -0,0 +1,53 @@ +[ + { + "name": "Organizations", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Organization" + ] + } + }, + "variable": { + "name": "Organization", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Repositories", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Repository" + ] + } + }, + "variable": { + "name": "Repository", + "allowMultipleSelection": true, + "default": "none", + "type": "object" + } + }, + { + "name": "Users", + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "User" + ] + } + }, + "variable": { + "name": "User", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/CodeRabbit/v1/defaultContent/security.dash.json b/plugins/CodeRabbit/v1/defaultContent/security.dash.json new file mode 100644 index 00000000..3808970c --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/security.dash.json @@ -0,0 +1,562 @@ +{ + "name": "Security", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "82742b42-02ba-495e-9abe-1fd3f30ee9ba", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Open Findings", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "Open Findings" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "39573536-5645-4303-9a37-7d324e9bcce6", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Critical and High Findings", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "severityRank", + "operation": "greaterthan", + "value": "3" + } + ] + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "Critical and High Findings" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "1826fe15-de25-4643-90aa-bd136f0e5b3d", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Repositories Affected", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "repository_id" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "repository_id_distinctCount", + "type": "quantitative", + "title": "Repositories Affected" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "0d69bdc8-2abe-461d-ac07-b4a6fb045852", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "CWE Types", + "description": "Distinct weakness types among open findings.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "distinctCount", + "names": [ + "cwe" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "cwe_distinctCount", + "type": "quantitative", + "title": "CWE Types" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "7c2e986d-002f-429d-8f66-0c3758225978", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Findings by Severity", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [ + [ + "severity", + "uniqueValues" + ], + [ + "severityRank", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "severityRank_uniqueValues", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "severity_uniqueValues", + "type": "nominal", + "title": "Severity" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Findings" + } + }, + "style": { + "legendPosition": "none", + "showLabels": true, + "colors": [ + "#FF570A" + ], + "cartesianColorRules": [ + { + "gte": 0, + "color": "#FF570A" + } + ] + } + } + } + } + }, + { + "i": "4666ae0a-663d-4c14-80de-134165b93f5d", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Findings by CWE", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "group": { + "by": [ + [ + "cwe", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "count", + "desc" + ] + ], + "top": 10 + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "cwe_uniqueValues", + "type": "nominal", + "title": "CWE" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Findings" + } + }, + "style": { + "cartesianHorizontal": true, + "legendPosition": "none", + "showLabels": true, + "colors": [ + "#FF570A" + ], + "cartesianColorRules": [ + { + "gte": 0, + "color": "#FF570A" + } + ] + } + } + } + } + }, + { + "i": "cd0d4ef1-c8f9-4535-bb18-47faebeea174", + "x": 0, + "y": 6, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Findings", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[Repository]}}" + ], + "dataStream": { + "id": "{{dataStreams.[securityFindings]}}", + "name": "securityFindings", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "state": "open", + "repository": { + "variable": "{{variables.[Repository]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Repositories]}}" + } + }, + "sort": { + "by": [ + [ + "severityRank", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "severity", + "title": "Severity", + "renderer": { + "type": "text" + }, + "width": 100 + }, + { + "field": "summary", + "title": "Finding", + "renderer": { + "type": "text" + } + }, + { + "field": "cwe", + "title": "CWE", + "renderer": { + "type": "text" + }, + "width": 110 + }, + { + "field": "repository_name", + "title": "Repository", + "renderer": { + "type": "text" + }, + "width": 180 + }, + { + "field": "file_path", + "title": "File", + "renderer": { + "type": "text" + } + }, + { + "field": "start_line", + "title": "Line", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 80 + } + ] + }, + "style": { + "richTableSort": { + "field": "severityRank", + "direction": "desc" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/CodeRabbit/v1/defaultContent/user.dash.json b/plugins/CodeRabbit/v1/defaultContent/user.dash.json new file mode 100644 index 00000000..31515b88 --- /dev/null +++ b/plugins/CodeRabbit/v1/defaultContent/user.dash.json @@ -0,0 +1,609 @@ +{ + "name": "User", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": [ + "{{variables.[User]}}" + ], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "7b17584f-5583-46ac-ade4-5e3424965c10", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "User Details", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "timeframe": "none", + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Users]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[User]}}" + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "name", + "title": "Name", + "renderer": { + "type": "text" + } + }, + { + "field": "seatAssigned", + "title": "Seat Assigned", + "renderer": { + "type": "boolean", + "trueLabel": "Yes", + "falseLabel": "No" + } + }, + { + "field": "role", + "title": "Role", + "renderer": { + "type": "text" + } + } + ] + }, + "style": { + "richTableTranspose": true, + "richTableFontSize": 14, + "richTableCellPadding": 8 + } + } + } + } + }, + { + "i": "074b642d-c67f-4812-a9e7-a519d6e7f75a", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "PRs Merged", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "count", + "type": "quantitative", + "title": "PRs Merged" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "d0312110-a50a-4382-9674-c50d6b2573ec", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Acceptance Rate", + "description": "Share of CodeRabbit findings that were accepted.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "datastream-sql", + "dataSourceConfig": { + "version": "2.0", + "sql": "SELECT AVG(\"acceptedCount\") * 100 AS \"acceptanceRate\" FROM \"dataset1\"", + "tables": [ + { + "tableName": "dataset1", + "config": { + "variables": [ + "{{variables.[User]}}" + ], + "activePluginConfigIds": [ + "{{configId}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + } + } + } + } + ] + }, + "metadata": [ + { + "name": "acceptanceRate", + "shape": [ + "percent", + { + "decimalPlaces": 1 + } + ] + }, + { + "pattern": ".*" + } + ] + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "gauge", + "encoding": { + "value": { + "field": "acceptanceRate", + "type": "quantitative", + "title": "Acceptance Rate" + } + }, + "style": { + "gaugeMin": 0, + "gaugeMax": 100, + "gaugeBands": [ + { + "to": 0.6, + "color": "#DE0038" + }, + { + "to": 0.8, + "color": "#E18700" + }, + { + "to": 1, + "color": "#259A51" + } + ] + } + } + } + } + }, + { + "i": "b5ac9a89-ef60-4eda-815a-8ac5b73b550b", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Median Time to Merge", + "description": "Hours from ready for review to merge.", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + }, + "group": { + "by": [], + "aggregate": [ + { + "type": "median", + "names": [ + "hoursToMerge" + ] + } + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "scalar", + "encoding": { + "values": [ + { + "field": "hoursToMerge_median", + "type": "quantitative", + "title": "Hours" + } + ] + }, + "style": { + "scalarLabelPosition": "none" + } + } + } + } + }, + { + "i": "f0097e03-b7c1-4763-bf7c-b55619e59e61", + "x": 0, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "PRs Merged Per Day", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + }, + "group": { + "by": [ + [ + "merged_at", + "byDay" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "merged_at_byDay", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "merged_at_byDay", + "type": "temporal", + "title": "Merged" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "PRs Merged" + } + }, + "style": { + "legendPosition": "none", + "colors": [ + "#FF570A" + ], + "cartesianColorRules": [ + { + "gte": 0, + "color": "#FF570A" + } + ] + } + } + } + } + }, + { + "i": "b987ee78-40c3-421c-8f7a-64ae40140247", + "x": 2, + "y": 2, + "w": 2, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Findings by Severity", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "{{dataStreams.[reviewComments]}}", + "name": "reviewComments", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + }, + "group": { + "by": [ + [ + "severity", + "uniqueValues" + ], + [ + "severityRank", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + }, + "sort": { + "by": [ + [ + "severityRank_uniqueValues", + "asc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "bar", + "encoding": { + "x": { + "field": "severity_uniqueValues", + "type": "nominal", + "title": "Severity" + }, + "y": { + "field": "count", + "type": "quantitative", + "title": "Findings" + } + }, + "style": { + "legendPosition": "none", + "showLabels": true, + "colors": [ + "#FF570A" + ], + "cartesianColorRules": [ + { + "gte": 0, + "color": "#FF570A" + } + ] + } + } + } + } + }, + { + "i": "705c2ef5-a1e1-4223-b711-46189a4ef1e3", + "x": 0, + "y": 6, + "w": 4, + "h": 5, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Recent Pull Requests", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "variables": [ + "{{variables.[User]}}" + ], + "dataStream": { + "id": "{{dataStreams.[pullRequests]}}", + "name": "pullRequests", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "status": "merged", + "user": { + "variable": "{{variables.[User]}}", + "workspace": "{{workspaceId}}", + "scope": "{{scopes.[Users]}}" + } + }, + "sort": { + "by": [ + [ + "merged_at", + "desc" + ] + ] + } + }, + "vizSpec": { + "version": "1.0", + "visualization": { + "chartType": "richTable", + "encoding": { + "columns": [ + { + "field": "prUrl", + "title": "Pull Request", + "renderer": { + "type": "link", + "labelField": "prNumber" + }, + "width": 110 + }, + { + "field": "repository_name", + "title": "Repository", + "renderer": { + "type": "text" + } + }, + { + "field": "author_username", + "title": "Author", + "renderer": { + "type": "text" + } + }, + { + "field": "merged_at", + "title": "Merged", + "renderer": { + "type": "date" + }, + "width": 170 + }, + { + "field": "hoursToMerge", + "title": "Hours to Merge", + "renderer": { + "type": "number", + "decimals": 1 + }, + "align": "right", + "width": 130 + }, + { + "field": "review_iterations", + "title": "Review Iterations", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 140 + }, + { + "field": "commentsPosted", + "title": "Comments Posted", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 140 + }, + { + "field": "commentsAccepted", + "title": "Comments Accepted", + "renderer": { + "type": "number", + "decimals": 0 + }, + "align": "right", + "width": 150 + } + ] + }, + "style": { + "richTableSort": { + "field": "merged_at", + "direction": "desc" + } + } + } + } + } + } + ] + } +} diff --git a/plugins/CodeRabbit/v1/docs/README.md b/plugins/CodeRabbit/v1/docs/README.md new file mode 100644 index 00000000..4dd8112d --- /dev/null +++ b/plugins/CodeRabbit/v1/docs/README.md @@ -0,0 +1,68 @@ +Track [CodeRabbit](https://www.coderabbit.ai) seat usage, adoption, pull request review times, review quality and security findings in SquaredUp, via the [CodeRabbit API](https://docs.coderabbit.ai/api-reference). + +> ⚠️ The CodeRabbit API is only available on the **Enterprise** plan. + +## Setup + +You will need a CodeRabbit **API key**, created by an organization admin. + +1. Sign in to [CodeRabbit](https://app.coderabbit.ai) as an organization **admin**. Keys created by other roles cannot read seat and license data. +2. Go to **Organization Settings > [API Keys](https://app.coderabbit.ai/settings/api-keys)** and create a new key. +3. To see AI Deep Scan results on the **Security** dashboard, give the key the **security** read permission. +4. Copy the key and paste it into the **API key** field. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ---------------- | -------- | +| **API key** | Authenticates every request to the CodeRabbit API. | CodeRabbit > **Organization Settings > API Keys**. | Yes | + +On save, the data source lists your CodeRabbit organizations to check the key, then checks that seat data can be read. An invalid key, or an organization that is not on the Enterprise plan, fails setup. A key created without admin rights still connects, but seat and license data is not available. + +## What this data source contains + +- **Seats and licenses**: seats purchased, seats assigned, seats available and which users hold a seat. +- **Adoption**: active users and repositories, pull requests per user and per repository, and seats held by users with no merged pull requests. +- **Review speed**: time from ready for review to first human review, to last commit and to merge, plus CodeRabbit's estimated review time and complexity. +- **Review quality**: every finding CodeRabbit raised, its severity and category, and whether the author accepted it. +- **Security**: AI Deep Scan findings by severity, CWE, repository and file. +- **MCP usage**: which MCP servers CodeRabbit called during reviews, with tool call and insight counts. +- **Admin activity**: the organization audit log. + +The out-of-the-box dashboards are **Overview**, **Licenses**, **PRs & Reviews**, **Security** and a **User** perspective. + +## Data streams + +**Organization streams: no object selection needed** + +- **Licenses**: seats purchased, seats assigned, seats available, seat utilization and the seat assignment mode. +- **Users**: every CodeRabbit user with their role and whether they hold a seat. +- **Organizations**: the CodeRabbit organizations the API key can access, with their Git provider. +- **Audit logs**: administrative actions in the organization, with the actor, resource and IP address. Supports a free text search. + +**Review streams: optionally filtered to one or more repositories or users** + +- **Pull requests**: merged or closed pull requests reviewed by CodeRabbit, one row per pull request, with review timings and comment counts. +- **Review comments**: findings CodeRabbit raised on merged pull requests, one row per finding, with severity, category and whether it was accepted. +- **MCP usage**: one row per MCP server used on a pull request, with insight and tool call counts. +- **Security findings**: AI Deep Scan findings, filtered by state (open, fixed, dismissed or all). + +**Repository streams: for one organization** + +- **Repositories**: repositories CodeRabbit records for an organization, and whether CodeRabbit is installed on each. + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Organization** | `GET /v1/organizations` | A CodeRabbit organization, linked to an organization in your Git provider. | +| **Repository** | `GET /v1/organizations/{id}/repositories` | A repository in an organization, with its installation status and parent organization. | +| **User** | `GET /v1/users` | A CodeRabbit user, with their role and seat assignment. | + +## Notes + +- **Review data is reported by day**: CodeRabbit filters pull requests and findings by date, not time. For **Last 24 hours**, merged pull requests are trimmed to the exact window; closed pull requests are reported for the whole day. +- **Review data covers up to the last quarter**: the review streams offer timeframes from **Last 24 hours** (**Last 7 days** for MCP usage) up to **Last quarter**. +- **Security findings need the security permission**: without it, the Security findings stream reports a permissions error. +- **MCP usage must be enabled**: if MCP usage metrics are not enabled for your CodeRabbit organization, the MCP usage stream says so rather than returning rows. +- **Read-only**: the data source never creates, modifies or deletes anything in CodeRabbit. diff --git a/plugins/CodeRabbit/v1/icon.svg b/plugins/CodeRabbit/v1/icon.svg new file mode 100644 index 00000000..2de58abc --- /dev/null +++ b/plugins/CodeRabbit/v1/icon.svg @@ -0,0 +1 @@ + diff --git a/plugins/CodeRabbit/v1/indexDefinitions/default.json b/plugins/CodeRabbit/v1/indexDefinitions/default.json new file mode 100644 index 00000000..d243de90 --- /dev/null +++ b/plugins/CodeRabbit/v1/indexDefinitions/default.json @@ -0,0 +1,50 @@ +{ + "steps": [ + { + "name": "organizations", + "dataStream": { "name": "organizations" }, + "timeframe": "none", + "objectMapping": { + "id": "provider_organization_id", + "name": "name", + "type": { "value": "Organization" }, + "properties": ["provider", { "codeRabbitId": "id" }] + } + }, + { + "name": "repositories", + "dataStream": { "name": "repositories" }, + "scope": { + "query": "g.V().has(\"sourceType\", \"Organization\")" + }, + "timeframe": "none", + "objectMapping": { + "id": "repository_id", + "name": "repository_name", + "type": { "value": "Repository" }, + "properties": [ + "private", + { "groupPath": "group_path" }, + { "installed": "is_installed" }, + "organizationId", + "organizationName" + ] + }, + "dependsOn": ["organizations"] + }, + { + "name": "users", + "dataStream": { "name": "users" }, + "timeframe": "none", + "objectMapping": { + "id": "user_id", + "name": "username", + "type": { "value": "User" }, + "properties": [ + { "seatAssigned": "seat_assigned" }, + { "role": "role_name" } + ] + } + } + ] +} diff --git a/plugins/CodeRabbit/v1/metadata.json b/plugins/CodeRabbit/v1/metadata.json new file mode 100644 index 00000000..3d37c1f9 --- /dev/null +++ b/plugins/CodeRabbit/v1/metadata.json @@ -0,0 +1,50 @@ +{ + "name": "coderabbit", + "displayName": "CodeRabbit", + "version": "1.0.0", + "author": { "name": "@TimWheeler-SQUP", "type": "community" }, + "description": "Track CodeRabbit seat usage, adoption, pull request review times, review quality and security findings.", + "category": "Analytics", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "coderabbit", + "code review", + "ai", + "pull requests", + "developer productivity", + "seats", + "licenses", + "security" + ], + "objectTypes": ["Organization", "Repository", "User"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/CodeRabbit/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/CodeRabbit/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "https://api.coderabbit.ai", + "authMode": "none", + "headers": [ + { + "key": "x-coderabbitai-api-key", + "value": "{{apiKey}}" + } + ], + "queryArgs": [] + } + } +} diff --git a/plugins/CodeRabbit/v1/ui.json b/plugins/CodeRabbit/v1/ui.json new file mode 100644 index 00000000..8b31b929 --- /dev/null +++ b/plugins/CodeRabbit/v1/ui.json @@ -0,0 +1,10 @@ +[ + { + "type": "password", + "name": "apiKey", + "label": "API key", + "placeholder": "cr-xxxxxxxxxx", + "help": "Create an API key on the [API Keys](https://app.coderabbit.ai/settings/api-keys) page under **Organization Settings** in CodeRabbit, signed in as an organization admin", + "validation": { "required": true } + } +] From 77dec4d74ea200be7b7b8964ffebaaa32ffece2b Mon Sep 17 00:00:00 2001 From: Tim Wheeler Date: Fri, 16 Oct 2026 09:40:04 +0100 Subject: [PATCH 2/3] Request CodeRabbit plugin reviews from its author Co-Authored-By: Claude Opus 5.5 --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4c0b7722..efc36c6c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,6 +8,7 @@ plugins/* @squaredup/community-moderators # Request review from original author plugins/AutoTask/* @TimWheeler-SQUP +plugins/CodeRabbit/* @TimWheeler-SQUP plugins/DattoRMM/* @TimWheeler-SQUP plugins/DigiCert/* @shaswot77 plugins/Huntress/* @Deenk From 711bc4bd79ab1d3df158150a09292f965c7aa0d2 Mon Sep 17 00:00:00 2001 From: Tim Wheeler Date: Fri, 16 Oct 2026 14:46:29 +0100 Subject: [PATCH 3/3] Clarify that audit log search matches user names The CodeRabbit audit logs API only matches the search text against the actor's name, case-insensitively and on partial or full names. The field was labelled "Search", which suggested it searched every column. Rename it, add a tooltip, and correct the README description. Co-Authored-By: Claude Opus 5.5 --- plugins/CodeRabbit/v1/dataStreams/auditLogs.json | 5 +++-- plugins/CodeRabbit/v1/docs/README.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/CodeRabbit/v1/dataStreams/auditLogs.json b/plugins/CodeRabbit/v1/dataStreams/auditLogs.json index 978f1f09..00d3baae 100644 --- a/plugins/CodeRabbit/v1/dataStreams/auditLogs.json +++ b/plugins/CodeRabbit/v1/dataStreams/auditLogs.json @@ -31,8 +31,9 @@ { "type": "text", "name": "search", - "label": "Search", - "placeholder": "Enter text to search" + "label": "Search user name", + "placeholder": "Enter a user name", + "help": "Supports partial or full name matching" } ], "metadata": [ diff --git a/plugins/CodeRabbit/v1/docs/README.md b/plugins/CodeRabbit/v1/docs/README.md index 4dd8112d..1c341b67 100644 --- a/plugins/CodeRabbit/v1/docs/README.md +++ b/plugins/CodeRabbit/v1/docs/README.md @@ -38,7 +38,7 @@ The out-of-the-box dashboards are **Overview**, **Licenses**, **PRs & Reviews**, - **Licenses**: seats purchased, seats assigned, seats available, seat utilization and the seat assignment mode. - **Users**: every CodeRabbit user with their role and whether they hold a seat. - **Organizations**: the CodeRabbit organizations the API key can access, with their Git provider. -- **Audit logs**: administrative actions in the organization, with the actor, resource and IP address. Supports a free text search. +- **Audit logs**: administrative actions in the organization, with the actor, resource and IP address. Supports filtering by user name, with partial or full name matching. **Review streams: optionally filtered to one or more repositories or users**