Skip to content

Conversation

@AlexSim93
Copy link
Owner

@AlexSim93 AlexSim93 commented Nov 24, 2025

Pull Request

Description

Adds support for teams in the EXCLUDE_USERS and INCLUDE_USERS parameters. Now, in addition to individual users, you can list teams separated by commas.

Related Issue(s)

Type of Change

  • Bug fix
  • New feature
  • Documentation update
  • Other (please specify)

How Has This Been Tested?

Run action on a test data.

Checklist:

  • I have read the CONTRIBUTING document.
  • My code follows the code style of this project.
  • I have added tests to cover my changes.
  • My changes generate no new warnings.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • Refactor
    • Extended eligibility checks to account for team memberships alongside individual user names when evaluating inclusion and exclusion criteria across pull request reviews and related metrics.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

This pull request extends user inclusion/exclusion filtering to support team-aware logic. Functions across the codebase are updated to accept a teams parameter and propagate it through checkUserInclusive calls, allowing filtering decisions to consider both individual users and their team memberships.

Changes

Cohort / File(s) Summary
Core inclusion logic
src/converters/utils/calculations/checkUserInclusive.ts
Updated signature to accept teams parameter; exclusion/inclusion checks now evaluate both user names and associated team names against EXCLUDE_USERS and INCLUDE_USERS lists
Approval and response calculations
src/converters/utils/calculations/getApproveTime.ts, src/converters/utils/calculations/getResponses.ts
Both functions updated to accept teams parameter and forward it to checkUserInclusive calls for team-aware eligibility filtering
Primary action preparation
src/converters/utils/prepareActionsTime.ts, src/converters/utils/preparePullRequestTimeline.ts
Functions extended with teams parameter; propagate it to all downstream checkUserInclusive and helper function calls
Review and discussion tracking
src/converters/utils/prepareReviews.ts, src/converters/utils/prepareConductedReviews.ts, src/converters/utils/prepareDiscussions.ts, src/converters/utils/prepareRequestedReviews.ts
All checkUserInclusive invocations updated to pass teams for consistent team-aware filtering across review tracking logic
Response time tracking
src/converters/utils/prepareResponseTime.ts
Updated to pass teams to both getResponses and checkUserInclusive calls for team-aware response time calculations
Data collection entry point
src/converters/collectData.ts
Updated call sites to forward teams parameter to prepareActionsTime, checkUserInclusive, and preparePullRequestTimeline

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Key focus areas:
    • Verify that checkUserInclusive correctly handles the team-aware logic (both exclusion and inclusion paths consider teams)
    • Confirm consistent propagation of the teams parameter across all call sites—several files updated with identical patterns
    • Check that no call sites to modified functions were missed, particularly in nested or conditional blocks
    • Validate that the teams structure (Record<string, string[]>) is correctly passed from the entry point through all layers

Possibly related PRs

Poem

🐰 A team's strength is now counted true,
No longer hidden from the view,
Through metrics, filters, checks galore,
Each rabbit crew adds to the score! 🌰

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature: adding support for teams in EXCLUDE_USERS/INCLUDE_USERS parameters, which is the primary change across all modified files.
Linked Issues check ✅ Passed All code changes implement the requested feature from issue #72: teams are now propagated through functions and considered in inclusion/exclusion logic alongside individual users.
Out of Scope Changes check ✅ Passed All changes are directly scoped to propagating the teams parameter and updating checkUserInclusive logic to consider team memberships—no out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-72/add-exclude-teams-support

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/converters/utils/prepareReviews.ts (1)

16-44: Fix teamNames accumulation to avoid dropping some team metrics

The new checkUserInclusive(userLogin, teams) calls look good, but the existing teamNames handling is problematic:

  • teamNames is reassigned on every iteration of the reduce, so after the loop it only contains the teams of the last included reviewer.
  • When iterating users, earlier team keys not in that last set fall into the !teamNames.includes(user) branch and end up with userReviews filtered by userLogin === user, which yields no reviews and effectively drops team-level metrics for those teams.

This likely skews reviewsConducted for scenarios with multiple distinct teams.

A minimal fix is to accumulate all team names instead of overwriting:

-  let teamNames: string[] = [];
-  const users = Object.keys(
-    reviews?.reduce((acc, review) => {
+  const teamNamesSet = new Set<string>();
+  const users = Object.keys(
+    reviews?.reduce((acc, review) => {
       const userLogin = review.user?.login || invalidUserLogin;
       if (userLogin !== pullRequestLogin && checkUserInclusive(userLogin, teams)) {
         const teamsNames = (teams[userLogin] || []).reduce(
           (acc, team) => ({ ...acc, [team]: 1 }),
           {}
         );
-        teamNames = Object.keys(teamsNames);
+        Object.keys(teamsNames).forEach((team) => teamNamesSet.add(team));
         return { ...acc, [userLogin]: 1, ...teamsNames, total: 1 };
       }
       return acc;
     }, {}) || {}
   );
+
+  const teamNames = Array.from(teamNamesSet);

This preserves your existing structure while ensuring all team keys are correctly recognized as teams during the users.forEach phase.

src/converters/utils/calculations/checkUserInclusive.ts (1)

3-27: Team-aware inclusion/exclusion logic is sound; cache inputs for performance

The extended logic correctly:

  • Excludes a user if they or any of their teams are in EXCLUDE_USERS.
  • Includes a user only if they or any of their teams are in INCLUDE_USERS when an include list is present, with EXCLUDE taking precedence.

Given how often this helper is called, it would be better to avoid recomputing the inputs on every branch:

-export const checkUserInclusive = (
-  name: string,
-  teams: Record<string, string[]>
-) => {
-  if (
-    getMultipleValuesInput("EXCLUDE_USERS").length === 0 &&
-    getMultipleValuesInput("INCLUDE_USERS").length === 0
-  ) {
+export const checkUserInclusive = (
+  name: string,
+  teams: Record<string, string[]>
+) => {
+  const excluded = getMultipleValuesInput("EXCLUDE_USERS");
+  const included = getMultipleValuesInput("INCLUDE_USERS");
+
+  if (excluded.length === 0 && included.length === 0) {
     return true;
   }
   if (
-    getMultipleValuesInput("EXCLUDE_USERS").length > 0 &&
-    (getMultipleValuesInput("EXCLUDE_USERS").includes(name) ||
-      teams[name]?.some((team) =>
-        getMultipleValuesInput("EXCLUDE_USERS").includes(team)
-      ))
+    excluded.length > 0 &&
+    (excluded.includes(name) ||
+      teams[name]?.some((team) => excluded.includes(team)))
   ) {
     return false;
   }
-  return getMultipleValuesInput("INCLUDE_USERS").length > 0
-    ? getMultipleValuesInput("INCLUDE_USERS").includes(name) ||
-        teams[name]?.some((team) =>
-          getMultipleValuesInput("INCLUDE_USERS").includes(team)
-        )
+  return included.length > 0
+    ? included.includes(name) ||
+        teams[name]?.some((team) => included.includes(team))
     : true;
 };

Functionally equivalent, but avoids repeated I/O/parsing on every call.

src/converters/utils/prepareResponseTime.ts (1)

101-147: Consider hoisting the inclusion check for efficiency.

The checkUserInclusive(user, teams) call at Line 102 is executed inside the userKey loop, resulting in redundant checks with identical arguments. Since user (the reviewer) doesn't change within this loop, consider moving the check outside:

 ["total", dateKey].forEach((key) => {
   const timeFromInitialRequestToResponse = calcDifferenceInMinutes(
     responses[0]?.[0],
     responses[0]?.[1],
     {
       endOfWorkingTime: getValueAsIs("CORE_HOURS_END"),
       startOfWorkingTime: getValueAsIs("CORE_HOURS_START"),
     },
     getMultipleValuesInput("HOLIDAYS")
   );

   const timeFromOpenToResponse = calcDifferenceInMinutes(
     pullRequest?.created_at,
     responses[0]?.[1],
     {
       endOfWorkingTime: getValueAsIs("CORE_HOURS_END"),
       startOfWorkingTime: getValueAsIs("CORE_HOURS_START"),
     },
     getMultipleValuesInput("HOLIDAYS")
   );

   const timeFromRepeatedRequestToResponse = responses
     .filter((el, index) => index > 0)
     .map((element) =>
       calcDifferenceInMinutes(
         element?.[0],
         element?.[1],
         {
           endOfWorkingTime: getValueAsIs("CORE_HOURS_END"),
           startOfWorkingTime: getValueAsIs("CORE_HOURS_START"),
         },
         getMultipleValuesInput("HOLIDAYS")
       )
     );

+  if (!checkUserInclusive(user, teams)) {
+    return;
+  }
+
   ["total", user, ...(teams[user] || [])].forEach((userKey) => {
-    if (checkUserInclusive(user, teams)) {
       set(collection, [userKey, key], {
         ...get(collection, [userKey, key], {}),
         timeFromInitialRequestToResponse:
           typeof timeFromInitialRequestToResponse === "number"
             ? [
                 ...get(
                   collection,
                   [userKey, key, "timeFromInitialRequestToResponse"],
                   []
                 ),
                 timeFromInitialRequestToResponse,
               ]
             : get(
                 collection,
                 [userKey, key, "timeFromInitialRequestToResponse"],
                 []
               ),
         timeFromOpenToResponse:
           typeof timeFromOpenToResponse === "number"
             ? [
                 ...get(
                   collection,
                   [userKey, key, "timeFromOpenToResponse"],
                   []
                 ),
                 timeFromOpenToResponse,
               ]
             : get(
                 collection,
                 [userKey, key, "timeFromOpenToResponse"],
                 []
               ),
         timeFromRepeatedRequestToResponse: [
           ...get(
             collection,
             [userKey, key, "timeFromRepeatedRequestToResponse"],
             []
           ),
           ...(timeFromRepeatedRequestToResponse.filter(
             (el) => typeof el === "number"
           ) as number[]),
         ],
       });
-    }
   });
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01d9a21 and 40834b7.

⛔ Files ignored due to path filters (4)
  • README.md is excluded by none and included by none
  • build/index.js is excluded by !build/** and included by none
  • package-lock.json is excluded by !**/package-lock.json and included by none
  • package.json is excluded by none and included by none
📒 Files selected for processing (11)
  • src/converters/collectData.ts (3 hunks)
  • src/converters/utils/calculations/checkUserInclusive.ts (2 hunks)
  • src/converters/utils/calculations/getApproveTime.ts (2 hunks)
  • src/converters/utils/calculations/getResponses.ts (3 hunks)
  • src/converters/utils/prepareActionsTime.ts (2 hunks)
  • src/converters/utils/prepareConductedReviews.ts (1 hunks)
  • src/converters/utils/prepareDiscussions.ts (6 hunks)
  • src/converters/utils/preparePullRequestTimeline.ts (1 hunks)
  • src/converters/utils/prepareRequestedReviews.ts (2 hunks)
  • src/converters/utils/prepareResponseTime.ts (3 hunks)
  • src/converters/utils/prepareReviews.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
src/converters/utils/prepareActionsTime.ts (3)
src/converters/types.ts (1)
  • Collection (52-124)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/constants.ts (1)
  • invalidUserLogin (1-1)
src/converters/utils/prepareRequestedReviews.ts (1)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/utils/preparePullRequestTimeline.ts (5)
src/converters/types.ts (1)
  • Collection (52-124)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/constants.ts (1)
  • invalidUserLogin (1-1)
src/converters/utils/calculations/getApproveTime.ts (1)
  • getApproveTime (6-58)
src/common/utils/index.ts (1)
  • getValueAsIs (2-2)
src/converters/utils/calculations/getApproveTime.ts (1)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/utils/prepareResponseTime.ts (2)
src/converters/utils/calculations/getResponses.ts (1)
  • getResponses (9-55)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/utils/calculations/checkUserInclusive.ts (1)
src/converters/utils/calculations/index.ts (1)
  • checkUserInclusive (14-14)
src/converters/utils/prepareDiscussions.ts (2)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/constants.ts (1)
  • invalidUserLogin (1-1)
src/converters/utils/prepareConductedReviews.ts (1)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/utils/calculations/getResponses.ts (3)
src/converters/utils/calculations/index.ts (2)
  • getResponses (12-12)
  • checkUserInclusive (14-14)
src/converters/constants.ts (2)
  • reviewRequestedTimelineEvent (4-4)
  • invalidUserLogin (1-1)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/collectData.ts (2)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/utils/calculations/index.ts (1)
  • checkUserInclusive (14-14)
src/converters/utils/prepareReviews.ts (2)
src/converters/utils/calculations/checkUserInclusive.ts (1)
  • checkUserInclusive (3-28)
src/converters/constants.ts (1)
  • invalidUserLogin (1-1)
🔇 Additional comments (12)
src/converters/utils/calculations/getApproveTime.ts (1)

6-10: Team-aware filtering of approvals looks correct

Passing teams into checkUserInclusive before updating statuses ensures excluded users/teams no longer affect approval time, and the updated function signature is consistent with upstream callers. No issues from this change.

Also applies to: 17-20

src/converters/utils/prepareConductedReviews.ts (1)

21-23: reviewsConducted now correctly respects team filters

Using checkUserInclusive(key, teams) in the loop ensures both per-user and per-team reviewsConducted entries honor EXCLUDE/INCLUDE team settings without changing existing "total" behavior.

src/converters/utils/calculations/getResponses.ts (1)

9-12: Response timelines now correctly honor team-based filters

Threading teams into getResponses and gating each event type with checkUserInclusive(user, teams) ensures that:

  • Requests to excluded users/teams are not tracked.
  • Reviews by excluded users/teams don’t close open request intervals.
  • Remove events don’t touch state for excluded users/teams.

The change is consistent with the updated checkUserInclusive semantics.

Also applies to: 15-17, 25-27, 39-41

src/converters/utils/preparePullRequestTimeline.ts (1)

21-36: Timeline metrics correctly wired to team-aware filters

Using checkUserInclusive with teams for the PR author and first reviewer, and passing teams into getApproveTime, ensures all time-to-* metrics ignore PRs and approvals from excluded users/teams. The updated signature and call sites are consistent.

src/converters/utils/prepareRequestedReviews.ts (1)

14-20: Requested-review stats now fully respect team filters

Gating both the construction of requestedReviewers and the final aggregation into collection with checkUserInclusive(user, teams) correctly:

  • Drops requests made to excluded users/teams.
  • Prevents rows for excluded teams/users from being created or incremented.

This aligns prepareRequestedReviews with the new team-aware inclusion logic elsewhere.

Also applies to: 30-33

src/converters/utils/prepareResponseTime.ts (2)

24-24: LGTM! Correct propagation of teams parameter.

The getResponses call is correctly updated to pass the teams parameter, enabling team-aware filtering of review responses.


47-47: LGTM! Correct bucket-level filtering.

The checkUserInclusive(userKey, teams) call correctly filters each bucket independently, including when userKey represents a team name from the spread ...(teams[user] || []).

src/converters/collectData.ts (2)

51-56: LGTM! Correct propagation of teams parameter.

The prepareActionsTime call is correctly updated to pass the teams parameter, enabling team-aware filtering of action time metrics.


70-95: Verify the intentional asymmetry between preparePullRequestInfo and preparePullRequestTimeline.

There's an asymmetry in how these two functions are called:

  • preparePullRequestInfo (Line 76) is only called when checkUserInclusive(userKey, teams) returns true (Line 72)
  • preparePullRequestTimeline (Line 86) is always called, though it receives the teams parameter for internal filtering

This means PR info (opened, closed counts, etc.) is filtered at this level, while timeline data is always collected and presumably filtered internally by preparePullRequestTimeline.

If this is intentional (e.g., timeline needs to aggregate data across all users for dependency tracking), please confirm. Otherwise, consider whether preparePullRequestTimeline should also be gated by the same inclusion check for consistency.

src/converters/utils/prepareActionsTime.ts (3)

8-15: LGTM! Function signature correctly extended.

The function signature is properly updated to include the teams parameter, aligning with the broader PR changes to support team-aware filtering.


30-36: LGTM! Correct filtering for reviewer actions.

The checkUserInclusive(user, teams) call correctly filters reviewer actions based on both individual user inclusion and team membership.


38-49: LGTM! Correct filtering for PR author actions.

Both checkUserInclusive(prAuthor, teams) calls correctly filter PR author actions (opening and merging) based on individual user inclusion and team membership.

Comment on lines 18 to 32
const reviewComments = comments[index]?.filter(
(comment) =>
pullRequestLogin !== (comment.user?.login || invalidUserLogin) &&
checkUserInclusive(comment.user?.login || invalidUserLogin)
checkUserInclusive(comment.user?.login || invalidUserLogin, teams)
);

const discussions = comments[index]?.filter((comment) => {
const userLogin = comment.user?.login || invalidUserLogin;
return (
!comment.in_reply_to_id &&
pullRequestLogin !== userLogin &&
checkUserInclusive(userLogin)
checkUserInclusive(userLogin, teams)
);
});

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Team-aware filtering is mostly consistent; consider tightening it for ‘received’ discussion types

Passing teams into all checkUserInclusive calls in this file correctly filters:

  • Which commenters’ discussions/review comments are counted.
  • Whether the PR author’s own metrics (e.g., reviewComments) are updated at all.

One subtle gap remains around received discussion-type metrics:

  • In the loop over [pullRequestLogin, ...(teams[pullRequestLogin] || []), "total"], the guard is if (checkUserInclusive(userLogin, teams)), i.e., it only checks the commenter, not the recipient (pullRequestLogin or its teams).
  • With EXCLUDE_USERS containing a team T, PRs authored by members of T can still contribute rows for T in discussionsTypes.received when non-excluded users comment on them, because those comments pass the userLogin check.

If the intent of issue #72 is “no rows at all for excluded teams,” you likely want to also gate by the receiver/team key. For example:

-        [pullRequestLogin, ...(teams[pullRequestLogin] || []), "total"].forEach(
-          (userKey) => {
-            if (checkUserInclusive(userLogin, teams)) {
+        [pullRequestLogin, ...(teams[pullRequestLogin] || []), "total"].forEach(
+          (userKey) => {
+            if (
+              checkUserInclusive(userLogin, teams) &&
+              checkUserInclusive(userKey, teams)
+            ) {
               set(collection, [userKey, key, "discussionsTypes", type], {
                 ...
               });
             }
           }
         );

A similar pattern can be applied to other places where you iterate userKey over users/teams/"total" but only check userLogin. That way:

  • Excluded commenters don’t generate any metrics.
  • Excluded teams/users don’t get rows, even when non-excluded people interact with their PRs.

If the current behavior is intentional (e.g., you do want to see what excluded teams receive, but not what they conduct), it’d be good to document that explicitly.

Also applies to: 33-41, 86-90, 143-146, 162-163, 212-215

🤖 Prompt for AI Agents
In src/converters/utils/prepareDiscussions.ts around lines 18 to 32, the
filtering only validates the commenter via checkUserInclusive(commenter, teams)
but when you later iterate recipient keys (pullRequestLogin,
teams[pullRequestLogin], "total") you must also gate by the recipient/team key
so excluded teams/users do not get rows for received metrics; update the guard
to require checkUserInclusive(commenterLogin, teams) && (userKey === "total" ||
checkUserInclusive(userKey, teams)) (or equivalent logic that treats "total" as
always allowed), and apply the same recipient-aware check to the other noted
ranges (33-41, 86-90, 143-146, 162-163, 212-215).

@AlexSim93 AlexSim93 changed the base branch from master to v4 November 24, 2025 16:34
@AlexSim93 AlexSim93 merged commit c4d0c64 into v4 Nov 24, 2025
2 checks passed
@AlexSim93 AlexSim93 deleted the issue-72/add-exclude-teams-support branch November 24, 2025 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Idea: Add option to exclude teams from metrics

2 participants