Skip to content

Active organizations widget endpoint#78

Merged
borfast merged 1 commit intomainfrom
active-organizations-widget
Mar 10, 2025
Merged

Active organizations widget endpoint#78
borfast merged 1 commit intomainfrom
active-organizations-widget

Conversation

@borfast
Copy link
Copy Markdown
Contributor

@borfast borfast commented Mar 7, 2025

This adds the necessary endpoint for the active organizations widget.

This PR is built on the changes from #77, and it shouldn't be merged until after that one is merged and this one is rebase onto main. Even reviewing should be held back until the rebase, because right now this shows changes that belong to the previous PR in the chain.

Summary by CodeRabbit

  • New Features

    • Active organization metrics now allow filtering by time periods, offering more tailored insights.
    • Contributor leaderboard functionality has been enhanced to deliver more accurate contribution data.
  • Refactor

    • Replaced static sample data with dynamic, query-driven data retrieval for improved reliability.
    • Improved query validation and sanitization for cleaner and more robust data processing.

@borfast borfast self-assigned this Mar 7, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 7, 2025

Warning

Rate limit exceeded

@borfast has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 2a00032000ced6574b1aa4101f7e2eba80a9f0e0 and 23a6844.

📒 Files selected for processing (5)
  • frontend/app/components/modules/project/components/contributors/active-organizations.vue (1 hunks)
  • frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (2 hunks)
  • frontend/server/data/data-sources.ts (1 hunks)
  • frontend/server/data/tinybird/active-organizations-data-source.ts (1 hunks)
  • frontend/server/data/types.ts (1 hunks)

Walkthrough

This pull request modifies how active organizations and contributor leaderboard data are fetched. The UI component now sends a granularity parameter instead of interval. The active organizations API endpoint has been restructured to construct an ActiveOrganizationsFilter using dynamic query parameters with the Luxon library for date parsing, and it now includes standardized error handling. Additionally, the DataSource interface is enhanced with new fetch methods, while several new Tinybird data source files have been added to retrieve both active organizations and contributor leaderboard data. New enums and types have also been introduced for improved filter definitions.

Changes

File(s) Change Summary
frontend/app/components/.../active-organizations.vue Renamed API parameter from interval to granularity in the component's request configuration.
frontend/server/api/project/[slug]/contributors/(active-organizations.get.ts, contributor-leaderboard.get.ts) Active Organizations API: Replaced hardcoded mock data with dynamic filtering using an ActiveOrganizationsFilter, added date parsing and error handling.
Contributor Leaderboard API: Removed an extraneous comment without changing filter creation logic.
frontend/server/data/data-sources.ts Updated the DataSource interface and createDataSource function to include new methods: fetchActiveOrganizations and fetchContributorsLeaderboard.
frontend/server/data/tinybird/* active-contributors-data-source.ts: Updated mapping type to use ActiveContributorsDataPoint.
active-organizations-data-source.ts: New file implementing Tinybird querying for active organizations, processing counts and change percentages.
contributors-leaderboard-source.ts: New file for fetching and processing contributor leaderboard data from Tinybird.
tinybird.ts: Modified query filtering to exclude undefined, empty strings, and null values.
frontend/server/data/types.ts Added new enums and types for filtering active organizations (ActiveOrganizationsFilterGranularity) and for the contributors leaderboard (ContributorsLeaderboardFilterMetric, ContributorsLeaderboardFilter).

Sequence Diagram(s)

sequenceDiagram
    participant UI as UI Component
    participant AO_API as Active Organizations API
    participant DS as DataSource
    participant TAO as Tinybird Active Orgs Data Source
    participant Tinybird as Tinybird API

    UI->>AO_API: Request active organizations data\n(granularity, repository, startDate, endDate)
    AO_API->>DS: Call fetchActiveOrganizations(filter)
    DS->>TAO: Query active organizations with filter
    TAO->>Tinybird: Request data with processed query parameters
    Tinybird-->>TAO: Return raw data response
    TAO-->>DS: Return processed organizations data
    DS-->>AO_API: Deliver active organizations response
    AO_API-->>UI: Return final data response
Loading

Possibly related PRs

  • Search endpoint #68: Renames the interval parameter to granularity and introduces the ActiveOrganizationsFilter, directly affecting the active organizations data handling.
  • Refactor data sources #76: Involves refactoring of data sources for fetching active contributors by modifying parameter handling, which relates to the changes in this PR.

Suggested reviewers

  • gaspergrom
  • emlimlf

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@borfast borfast marked this pull request as ready for review March 7, 2025 14:08
@borfast borfast requested review from emlimlf and gaspergrom March 7, 2025 14:10
Copy link
Copy Markdown

@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: 7

🔭 Outside diff range comments (1)
frontend/server/data/tinybird/active-contributors-data-source.test.ts (1)

27-27: 💡 Verification agent

❓ Verification inconclusive

Verify function name consistency

The test is still using createActiveContributorsDataSource, but the new file exports a more generic createDataSource function. Consider updating the tests to align with the new API or ensure that the imported function is correctly aliased.


🏁 Script executed:

#!/bin/bash
# Check how createActiveContributorsDataSource is defined in data-sources.ts
rg "createActiveContributorsDataSource" frontend/server/data/data-sources.ts

Length of output: 76


Action required: Update test usage to match the new API

The file frontend/server/data/tinybird/active-contributors-data-source.test.ts still references the outdated function createActiveContributorsDataSource. Our search in frontend/server/data/data-sources.ts did not reveal any definition for this function, which suggests the new, more generic API (likely exporting createDataSource) is intended for use. Please update the test to either import and alias the new function or directly call createDataSource to ensure consistency.

  • File Affected: frontend/server/data/tinybird/active-contributors-data-source.test.ts (line 27)
  • Proposed Change:
    - const dataSource = createActiveContributorsDataSource(mockFetch);
    + const dataSource = createDataSource(mockFetch);
🧹 Nitpick comments (10)
frontend/server/data/data-sources.ts (1)

23-29: Good implementation of the factory pattern

The createDataSource function follows the factory pattern, providing a single point of creation for data sources. This approach simplifies data source management and makes it easier to swap implementations in the future.

Consider adding centralized error handling in this factory function to provide consistent error reporting across all data fetching operations:

export function createDataSource(): DataSource {
    return {
-        fetchActiveContributors,
-        fetchContributorsLeaderboard,
-        fetchActiveOrganizations
+        fetchActiveContributors: async (filter) => {
+            try {
+                return await fetchActiveContributors(filter);
+            } catch (error) {
+                console.error('Error fetching active contributors:', error);
+                throw error;
+            }
+        },
+        fetchContributorsLeaderboard: async (filter) => {
+            try {
+                return await fetchContributorsLeaderboard(filter);
+            } catch (error) {
+                console.error('Error fetching contributors leaderboard:', error);
+                throw error;
+            }
+        },
+        fetchActiveOrganizations: async (filter) => {
+            try {
+                return await fetchActiveOrganizations(filter);
+            } catch (error) {
+                console.error('Error fetching active organizations:', error);
+                throw error;
+            }
+        }
    };
}
frontend/server/api/project/[slug]/contributors/contributor-leaderboard.get.ts (1)

55-55: Ensure meta fields are populated correctly.

The static metadata values (offset, limit, total) in lines 37-41 should ideally be populated from the actual result returned by the data source.

 return {
-  meta,
+  meta: {
+    offset: result.meta.offset,
+    limit: result.meta.limit, 
+    total: result.meta.total
+  },
   data: result.data
 };
frontend/server/data/tinybird/active-organizations-data-source.ts (1)

77-85: Consider adding defensive coding for the data check.

The current implementation checks if data is undefined, but not if it's null or if data.data is undefined/null.

 let processedData: ActiveOrganizationsResponseData = [];
-if (data !== undefined) {
+if (data && (data as TinybirdResponse<TinybirdActiveOrganizationsData>)?.data) {
   processedData = (data as TinybirdResponse<TinybirdActiveOrganizationsData>)?.data.map(
     (item): ActiveOrganizationsDataPoint => ({
       startDate: item.startDate,
       endDate: item.endDate,
       organizations: item.organizationCount
     })
   );
 }
frontend/server/data/types.ts (1)

18-29: Consider unifying the property name for consistency with contributors
The ActiveOrganizationsFilter uses repository while ActiveContributorsFilter uses repo. If both filters represent similar repository-related constraints, ensuring consistent naming can improve readability and maintainability.

frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (3)

32-32: Address the project configuration TODO
A comment indicates a need to verify a project’s configuration before returning data. Consider implementing or tracking this requirement to avoid unexpected behavior for unconfigured projects.


46-47: Refactor repetitive query checks
You are conditionally setting repository, startDate, and endDate from query params multiple times. Consider extracting a small helper function to trim, check, and parse each param to reduce duplication and potential errors. Also, confirm how you want to handle invalid date strings returned by DateTime.fromISO().

Also applies to: 50-51, 54-55


58-69: Leverage finer-grained error handling
The current try-catch rethrows all exceptions as a 500 error. You could differentiate errors caused by invalid user input (e.g., a malformed date) versus genuine server issues, returning more precise status codes where relevant.

frontend/server/data/tinybird/active-contributors-data-source.ts (3)

15-31: Well-structured type definitions
The new response and data point interfaces improve clarity for consumers of active contributors data. Adding inline documentation may further boost maintainability.


46-47: Mitigate the security risk
The TODO highlights a valid concern about passing unchecked query parameters to Tinybird. Implemented parameter sanitization or validation logic would ensure safe usage of external services.


71-73: Multiple queries to the same endpoint
Fetching three subsets of data from the same endpoint is valid. However, monitor performance and consider caching frequently repeated queries to reduce latency or API consumption costs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 332b473 and 67648c564469b714b989320be529ed8385b66130.

📒 Files selected for processing (12)
  • frontend/app/components/modules/project/components/contributors/active-organizations.vue (1 hunks)
  • frontend/server/api/project/[slug]/contributors/active-contributors.get.ts (2 hunks)
  • frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (2 hunks)
  • frontend/server/api/project/[slug]/contributors/contributor-leaderboard.get.ts (2 hunks)
  • frontend/server/data/active-contributors-data-source.ts (0 hunks)
  • frontend/server/data/data-sources.ts (1 hunks)
  • frontend/server/data/tinybird/active-contributors-data-source.test.ts (1 hunks)
  • frontend/server/data/tinybird/active-contributors-data-source.ts (2 hunks)
  • frontend/server/data/tinybird/active-organizations-data-source.ts (1 hunks)
  • frontend/server/data/tinybird/contributors-leaderboard-source.ts (1 hunks)
  • frontend/server/data/tinybird/tinybird.ts (1 hunks)
  • frontend/server/data/types.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/server/data/active-contributors-data-source.ts
🔇 Additional comments (18)
frontend/server/data/tinybird/tinybird.ts (1)

41-41: Enhanced query filtering for better data quality

The enhanced filtering now removes undefined, empty strings, and null values from the query parameters. This improvement ensures cleaner and more predictable API requests to TinyBird.

frontend/server/data/tinybird/active-contributors-data-source.test.ts (2)

10-10: Updated import path for data source function

The import path has been updated to reflect the new location of the data source creation function in the consolidated data-sources.ts file.


15-15: Test suite name now more accurately describes what's being tested

Changing from 'ContributorsDataSource' to 'ActiveContributorsDataSource' provides a more specific and clearer description of the test suite's purpose.

frontend/server/data/data-sources.ts (2)

1-16: Well-structured imports and good documentation

The file starts with clear documentation about its purpose and imports all necessary types and functions in an organized manner.


17-21: Clean interface design for data source abstraction

The DataSource interface provides a well-defined contract for data access methods, making the code more maintainable and the interface between components clearer.

frontend/server/api/project/[slug]/contributors/active-contributors.get.ts (2)

28-28: Good refactoring to use a centralized data source.

The change from a specialized createActiveContributorsDataSource import to a more generic createDataSource aligns with best practices for maintainability.


59-59:

Details

✅ Verification successful

Verify all data source functionality remains intact.

The change to a generic data source factory function looks good, but ensure that any specific configuration or functionality previously handled by createActiveContributorsDataSource() has been properly migrated to the new implementation.


🏁 Script executed:

#!/bin/bash
# Check if the createDataSource function properly handles active contributors functionality
rg -A 5 "fetchActiveContributors" --type ts

Length of output: 3738


Data source functionality verified – All active contributors features are intact.

The generic createDataSource() implementation correctly integrates fetchActiveContributors with all associated configurations from the previous createActiveContributorsDataSource() implementation. The search results confirm that:

  • The DataSource interface in frontend/server/data/data-sources.ts includes fetchActiveContributors.
  • The implementation from frontend/server/data/tinybird/active-contributors-data-source.ts is being correctly utilized.
  • Tests in frontend/server/data/tinybird/active-contributors-data-source.test.ts validate that active contributors functionality is working as expected.

You can proceed with confidence that the new generic data source factory preserves the required behavior.

frontend/server/api/project/[slug]/contributors/contributor-leaderboard.get.ts (2)

1-4: Good addition of required imports.

The imports are appropriately organized to support the new data source functionality.


43-48: Filter construction looks good.

The filter object is properly constructed with appropriate fallbacks and type handling for the DateTime conversions.

frontend/server/data/tinybird/active-organizations-data-source.ts (2)

70-74: Good use of Promise.all for parallel requests.

Using Promise.all for concurrent API calls improves performance by fetching the required data in parallel.


91-97: Well-handled percentage change calculation with edge cases.

The percentage change calculation correctly handles the various edge cases, including when previous count is zero.

frontend/server/data/types.ts (1)

31-40: Verify whether 'repository' should be optional in the leaderboard filter
The repository field is mandatory here, contrasting with the optional field in ActiveOrganizationsFilter. If the leaderboard can display data for all repositories, consider making the field optional. Otherwise, confirm that you'll always pass a repository value.

frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (2)

19-23: New imports align with the revised data definitions
These imports cleanly integrate the updated types and data source creation. No issues found.


38-44: Revisit the default granularity
The filter defaults to QUARTERLY if none is provided. Confirm that this default aligns with product requirements or consider using a shorter interval if more frequent updates are desired.

frontend/server/data/tinybird/active-contributors-data-source.ts (4)

9-10: New import statements look consistent
Importing DateTime and ActiveContributorsFilter directly matches the new approach. No concerns here.


33-37: Tinybird type aliases are consistent
Defining TinybirdActiveContributorsSummary and TinybirdActiveContributorsData is a clear approach to keep external data typed. Looks good.


43-43: Exporting the fetch function
Exporting fetchActiveContributors openly facilitates reusability in other modules. Implementation shows no apparent issues.


78-79: Avoid potential null reference
When mapping data.data, ensure data and data.data exist before accessing them. A simple safety check can prevent runtime errors if Tinybird returns an empty response.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Parameter name change from 'interval' to 'granularity' improves API consistency

The parameter rename aligns with the backend filtering criteria in the ActiveOrganizationsFilter object. This change enhances naming consistency across the frontend and backend components.


🏁 Script executed:

#!/bin/bash
# Check for any other occurrences of 'interval' that might need to be updated for consistency
rg "interval.*value" -g "*.vue" -A 1 -B 1

Length of output: 708


Review: Parameter Naming Consistency in API-Critical Areas

  • The change in frontend/app/components/modules/project/components/contributors/active-organizations.vue (line 78) correctly updates the parameter from interval to granularity in accordance with the ActiveOrganizationsFilter criteria.
  • However, similar occurrences of interval: activeTab.value were detected in:
    • frontend/app/components/modules/project/components/popularity/github-mentions.vue
    • frontend/app/components/modules/project/components/popularity/social-mentions.vue

Please review these additional files to determine if they should also adopt the granularity naming for full cross-component consistency.

Comment on lines 50 to 51
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding error handling.

The data fetching implementation is missing error handling compared to the active-contributors endpoint.

 const dataSource = createDataSource();
-const result = await dataSource.fetchContributorsLeaderboard(filter);
+let result;
+try {
+  result = await dataSource.fetchContributorsLeaderboard(filter);
+} catch (error) {
+  console.error('Error fetching contributors leaderboard:', error);
+  throw createError({
+    statusCode: 500,
+    statusMessage: 'Failed to fetch contributors leaderboard data',
+    data: { message: error.message }
+  });
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const dataSource = createDataSource();
const result = await dataSource.fetchContributorsLeaderboard(filter);
const dataSource = createDataSource();
let result;
try {
result = await dataSource.fetchContributorsLeaderboard(filter);
} catch (error) {
console.error('Error fetching contributors leaderboard:', error);
throw createError({
statusCode: 500,
statusMessage: 'Failed to fetch contributors leaderboard data',
data: { message: error.message }
});
}

Comment on lines 46 to 47
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

❓ Verification inconclusive

Address the security TODO before production.

The TODO comment about passing unchecked query parameters to Tinybird directly from the frontend highlights a potential security risk. Consider implementing parameter validation or sanitization.


🏁 Script executed:

#!/bin/bash
# Check if there's any parameter validation or sanitization in the codebase
rg -A 3 "sanitize|validate" --type ts

Length of output: 39


Security Warning: Input Validation Required
The code in frontend/server/data/tinybird/active-organizations-data-source.ts (lines 46–47) still passes unchecked query parameters to TinyBird without any evidence of sanitization or validation. A search across the codebase for keywords like sanitize or validate returned no results, indicating that no handling is in place. Before moving to production, please implement robust input validation and sanitization to mitigate potential security risks (e.g., injection attacks).

  • Action needed:
    • Implement validation/sanitization logic for incoming query parameters in the above file.
    • Verify that all entry points that forward parameters to TinyBird enforce strict type and content validation.

Comment on lines 28 to 29
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address the security TODO before production.

Similar to the active-organizations module, there's a security concern about passing unchecked query parameters to Tinybird.

Consider implementing a parameter validation or sanitization layer before passing values to external services.

Comment on lines 51 to 57
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential reference error with data.rows.

There's a risk of a reference error when accessing data.rows if data is undefined.

 return {
   meta: {
     offset: 0,
     limit: 10,
-    total: data.rows
+    total: data && 'rows' in data ? data.rows : 0
   },
   data: processedData
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
meta: {
offset: 0,
limit: 10,
total: data.rows
},
data: processedData
return {
meta: {
offset: 0,
limit: 10,
total: data && 'rows' in data ? data.rows : 0
},
data: processedData
};

Comment on lines 39 to 49
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add robust error handling and data validation.

The data processing lacks comprehensive error handling for malformed or missing data.

 let processedData: ContributorsLeaderboardData = [];
-if (data !== undefined) {
+if (data && (data as TinybirdResponse<TinybirdContributorsLeaderboardData>)?.data) {
   processedData = (data as TinybirdResponse<TinybirdContributorsLeaderboardData>)?.data.map(
     (item): ContributorsLeaderboardDataPoint => ({
-      avatar: item.avatar,
-      name: item.displayName,
-      contributions: item.contributionCount,
+      avatar: item.avatar || undefined,
+      name: item.displayName || 'Unknown Contributor',
+      contributions: typeof item.contributionCount === 'number' ? item.contributionCount : 0,
       contributionValue: 0
     })
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let processedData: ContributorsLeaderboardData = [];
if (data !== undefined) {
processedData = (data as TinybirdResponse<TinybirdContributorsLeaderboardData>)?.data.map(
(item): ContributorsLeaderboardDataPoint => ({
avatar: item.avatar,
name: item.displayName,
contributions: item.contributionCount,
contributionValue: 0
})
);
}
let processedData: ContributorsLeaderboardData = [];
if (data && (data as TinybirdResponse<TinybirdContributorsLeaderboardData>)?.data) {
processedData = (data as TinybirdResponse<TinybirdContributorsLeaderboardData>)?.data.map(
(item): ContributorsLeaderboardDataPoint => ({
avatar: item.avatar || undefined,
name: item.displayName || 'Unknown Contributor',
contributions: typeof item.contributionCount === 'number' ? item.contributionCount : 0,
contributionValue: 0
})
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Clarify why contributionValue is set to 0.

The contributionValue is hardcoded to 0, but it's not clear if this is intentional or if it should be derived from the data.


🏁 Script executed:

#!/bin/bash
# Check if contributionValue is used elsewhere in the codebase
rg "contributionValue" --type ts --type vue

Length of output: 71


Below is a revised approach to verify the usage of contributionValue. The original command failed due to an unrecognized file type for Vue files. Please run the following updated shell script to search for references in both .ts and .vue files:

Once these commands are executed, review the output to confirm whether contributionValue is referenced elsewhere in the codebase. If no usage is found, it would be helpful to add an inline comment (or documentation) clarifying that the hardcoded value of 0 is an intentional default. This clarification will improve code readability and maintainability.

Please verify the results of the above script, and update the code or add comments accordingly.

Next Action Required:

  • Ensure the intended behavior of setting contributionValue to 0
  • Consider adding a comment that explains why it remains hardcoded when no derived value is available

🏁 Script executed:

#!/bin/bash
echo "Searching for 'contributionValue' in TypeScript (.ts) files:"
rg "contributionValue" --glob "*.ts"

echo "Searching for 'contributionValue' in Vue (.vue) files:"
rg "contributionValue" --glob "*.vue"

Length of output: 964


Clarify the rationale for the hardcoded value of contributionValue

It appears that in frontend/server/data/tinybird/contributors-leaderboard-source.ts (line 46) the field contributionValue is explicitly set to 0. Given that the type annotations elsewhere indicate it represents the “Value of the contribution,” please clarify whether this is intended as a default when no computed value is available or if it’s a temporary placeholder pending dynamic calculation. Adding an inline comment to document this decision would help improve code clarity.

  • File to update: frontend/server/data/tinybird/contributors-leaderboard-source.ts (line 46)

@borfast borfast force-pushed the active-organizations-widget branch from 67648c5 to f50b128 Compare March 10, 2025 23:09
Copy link
Copy Markdown

@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: 3

♻️ Duplicate comments (1)
frontend/server/data/tinybird/active-organizations-data-source.ts (1)

46-47: ⚠️ Potential issue

Address the security TODO before production.

The TODO comment about passing unchecked query parameters to Tinybird directly from the frontend highlights a potential security risk. Consider implementing parameter validation or sanitization.

Input validation should be implemented before this goes to production to prevent potential injection attacks. Consider:

  1. Adding input validation/sanitization for all parameters
  2. Creating a whitelist of allowed values for certain parameters
  3. Implementing type checking and boundary validation
🧹 Nitpick comments (5)
frontend/server/data/tinybird/active-organizations-data-source.ts (2)

87-97: Refactor percentage change calculation for better readability.

The percentage change calculation logic has multiple conditions and could be refactored for better readability and maintainability.

- const changeValue = currentOrganizationCount - previousOrganizationCount;
- let percentageChange = 0;
- if (previousOrganizationCount === 0 && currentOrganizationCount > 0) {
-   percentageChange = 100;
- } else if (previousOrganizationCount === 0 && currentOrganizationCount === 0) {
-   percentageChange = 0;
- } else if (previousOrganizationCount !== 0) {
-   percentageChange = ((currentOrganizationCount - previousOrganizationCount) / previousOrganizationCount) * 100;
- }
+ const changeValue = currentOrganizationCount - previousOrganizationCount;
+ let percentageChange = 0;
+ 
+ // Handle different scenarios for percentage change calculation
+ if (previousOrganizationCount === 0) {
+   percentageChange = currentOrganizationCount > 0 ? 100 : 0;
+ } else {
+   percentageChange = (changeValue / previousOrganizationCount) * 100;
+ }

76-85: Consider adding error handling for data processing.

The code processes data from the Tinybird response without explicit error handling for malformed data.

  let processedData: ActiveOrganizationsResponseData = [];
  if (data !== undefined) {
-   processedData = (data as TinybirdResponse<TinybirdActiveOrganizationsData>)?.data.map(
-     (item): ActiveOrganizationsDataPoint => ({
-       startDate: item.startDate,
-       endDate: item.endDate,
-       organizations: item.organizationCount
-     })
-   );
+   try {
+     processedData = (data as TinybirdResponse<TinybirdActiveOrganizationsData>)?.data.map(
+       (item): ActiveOrganizationsDataPoint => ({
+         startDate: item.startDate,
+         endDate: item.endDate,
+         organizations: item.organizationCount
+       })
+     );
+   } catch (error) {
+     console.error('Error processing Tinybird data:', error);
+     // Return empty array or throw a more specific error
+   }
  }
frontend/server/data/types.ts (2)

18-29: Consider reusing granularity enum to avoid duplication.

The ActiveOrganizationsFilterGranularity enum duplicates the values from ContributorsFilterGranularity. Consider reusing the existing enum to maintain consistency and reduce duplication.

-export enum ActiveOrganizationsFilterGranularity {
-  WEEKLY = 'weekly',
-  MONTHLY = 'monthly',
-  QUARTERLY = 'quarterly'
-}
+// Reuse the same granularity enum for both contributors and organizations
+export type ActiveOrganizationsFilterGranularity = ContributorsFilterGranularity;
 
 export type ActiveOrganizationsFilter = {
   project: string;
   repository?: string;
-  granularity?: ActiveOrganizationsFilterGranularity;
+  granularity?: ContributorsFilterGranularity;
   startDate?: DateTime;
   endDate?: DateTime;
 };

35-40: Ensure consistency in property naming across filter types.

There's an inconsistency between repository (used in ContributorsLeaderboardFilter) and repo (used in ActiveContributorsFilter). Consider standardizing property names across filter types.

 export type ContributorsLeaderboardFilter = {
   metric: ContributorsLeaderboardFilterMetric;
-  repository: string;
+  repo?: string;  // Change to match ActiveContributorsFilter, or update ActiveContributorsFilter to use repository
   startDate?: DateTime;
   endDate?: DateTime;
 }
frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (1)

61-69: Enhance error handling with more specific error cases.

The current error handling doesn't distinguish between different types of errors that might occur when fetching data.

 try {
   return await dataSource.fetchActiveOrganizations(filter);
 } catch (error) {
-  console.error('Error fetching active organizations:', error);
-  throw createError({
-    statusCode: 500,
-    statusMessage: 'Failed to fetch active organizations data',
-    data: { message: error.message }
-  });
+  console.error('Error fetching active organizations:', error);
+  
+  // Determine the appropriate error response based on the error type
+  if (error.name === 'FetchError' || error.name === 'TimeoutError') {
+    throw createError({
+      statusCode: 503,
+      statusMessage: 'Service temporarily unavailable',
+      data: { message: 'Unable to connect to data service', originalError: error.message }
+    });
+  } else if (error.name === 'TypeError' || error.name === 'SyntaxError') {
+    throw createError({
+      statusCode: 500,
+      statusMessage: 'Data processing error',
+      data: { message: 'Error processing response data', originalError: error.message }
+    });
+  } else {
+    throw createError({
+      statusCode: 500,
+      statusMessage: 'Failed to fetch active organizations data',
+      data: { message: error.message }
+    });
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67648c564469b714b989320be529ed8385b66130 and f50b1285120ddc8f37a608d98f829fe114d87999.

📒 Files selected for processing (13)
  • .git-branches.toml (1 hunks)
  • frontend/app/components/modules/project/components/contributors/active-organizations.vue (1 hunks)
  • frontend/server/api/project/[slug]/contributors/active-contributors.get.ts (2 hunks)
  • frontend/server/api/project/[slug]/contributors/active-organizations.get.ts (2 hunks)
  • frontend/server/api/project/[slug]/contributors/contributor-leaderboard.get.ts (2 hunks)
  • frontend/server/data/active-contributors-data-source.ts (0 hunks)
  • frontend/server/data/data-sources.ts (1 hunks)
  • frontend/server/data/tinybird/active-contributors-data-source.test.ts (1 hunks)
  • frontend/server/data/tinybird/active-contributors-data-source.ts (2 hunks)
  • frontend/server/data/tinybird/active-organizations-data-source.ts (1 hunks)
  • frontend/server/data/tinybird/contributors-leaderboard-source.ts (1 hunks)
  • frontend/server/data/tinybird/tinybird.ts (1 hunks)
  • frontend/server/data/types.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/server/data/active-contributors-data-source.ts
✅ Files skipped from review due to trivial changes (1)
  • .git-branches.toml
🚧 Files skipped from review as they are similar to previous changes (6)
  • frontend/app/components/modules/project/components/contributors/active-organizations.vue
  • frontend/server/data/tinybird/tinybird.ts
  • frontend/server/data/tinybird/active-contributors-data-source.test.ts
  • frontend/server/api/project/[slug]/contributors/contributor-leaderboard.get.ts
  • frontend/server/api/project/[slug]/contributors/active-contributors.get.ts
  • frontend/server/data/tinybird/contributors-leaderboard-source.ts
🔇 Additional comments (8)
frontend/server/data/data-sources.ts (1)

1-29: Well-organized centralized data source factory pattern implementation.

The implementation of a centralized factory pattern for data sources is a good architecture decision. This approach:

  1. Creates a clean abstraction layer between API endpoints and data sources
  2. Makes it easier to switch data sources in the future without changing API code
  3. Consolidates all data fetching methods in one interface
  4. Follows the Single Responsibility Principle

The DataSource interface clearly defines the contract that any implementation must follow, making the code more maintainable and testable.

frontend/server/data/tinybird/active-contributors-data-source.ts (7)

9-10: Import changes align with type refactoring.

The updated imports reflect the transition to using the new ActiveContributorsFilter type, which is part of the broader refactoring mentioned in the PR objectives.


15-31: Well-structured type definitions with clear documentation.

The new type definitions (ActiveContributorsDataPoint, ActiveContributorsResponseData, and ActiveContributorsResponse) provide a well-organized structure with proper documentation for each field. This enhances type safety and clarifies the data structure expected by consumers of this module.


33-33: Type renaming improves consistency.

Renaming TinybirdContributorsSummary to TinybirdActiveContributorsSummary is consistent with the naming pattern established in the new types and better reflects its specific purpose.


37-37: Type renaming improves consistency.

Similarly, renaming TinybirdContributorsData to TinybirdActiveContributorsData aligns with the consistent naming strategy across the codebase.


43-43: Function is now properly exported for external use.

The fetchActiveContributors function has been correctly exported to support the centralized data source creation pattern mentioned in the PR summary.


71-73: Updated type parameters in API calls.

The type parameters have been appropriately updated to use the renamed types, maintaining type safety while fetching data from Tinybird.


78-79: Mapping to new type structure.

The mapping logic has been properly adjusted to transform the Tinybird response into the new ActiveContributorsDataPoint type structure.

Comment on lines 50 to 56
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for start and end date relationship.

The code doesn't validate that the start date is before the end date, which could lead to unexpected results.

 if (query.startDate && (query.startDate as string).trim() !== '') {
   filter.startDate = DateTime.fromISO(query.startDate as string);
 }

 if (query.endDate && (query.endDate as string).trim() !== '') {
   filter.endDate = DateTime.fromISO(query.endDate as string);
 }
+
+// Validate date range
+if (filter.startDate && filter.endDate && filter.startDate > filter.endDate) {
+  throw createError({
+    statusCode: 400,
+    statusMessage: 'Invalid date range',
+    data: { message: 'Start date must be before end date' }
+  });
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (query.startDate && (query.startDate as string).trim() !== '') {
filter.startDate = DateTime.fromISO(query.startDate as string);
}
return data;
if (query.endDate && (query.endDate as string).trim() !== '') {
filter.endDate = DateTime.fromISO(query.endDate as string);
}
if (query.startDate && (query.startDate as string).trim() !== '') {
filter.startDate = DateTime.fromISO(query.startDate as string);
}
if (query.endDate && (query.endDate as string).trim() !== '') {
filter.endDate = DateTime.fromISO(query.endDate as string);
}
// Validate date range
if (filter.startDate && filter.endDate && filter.startDate > filter.endDate) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid date range',
data: { message: 'Start date must be before end date' }
});
}

Comment on lines 37 to 38
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement the TODO for query parameter validation.

The TODO comment indicates a need for query parameter validation, which is important for security and data integrity.

Consider implementing a validation function or middleware to validate query parameters before processing them:

+function validateQueryParams(query: any): string | null {
+  // Validate granularity
+  if (query.granularity && 
+      !Object.values(ActiveOrganizationsFilterGranularity).includes(query.granularity)) {
+    return `Invalid granularity: ${query.granularity}`;
+  }
+  
+  // Validate dates
+  if (query.startDate && !DateTime.fromISO(query.startDate as string).isValid) {
+    return `Invalid startDate: ${query.startDate}`;
+  }
+  
+  if (query.endDate && !DateTime.fromISO(query.endDate as string).isValid) {
+    return `Invalid endDate: ${query.endDate}`;
+  }
+  
+  return null;
+}

 // TODO: Validate the query params
+const validationError = validateQueryParams(query);
+if (validationError) {
+  throw createError({
+    statusCode: 400,
+    statusMessage: 'Invalid request parameters',
+    data: { message: validationError }
+  });
+}
 const filter: ActiveOrganizationsFilter = {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// TODO: Validate the query params
const filter: ActiveOrganizationsFilter = {
function validateQueryParams(query: any): string | null {
// Validate granularity
if (query.granularity &&
!Object.values(ActiveOrganizationsFilterGranularity).includes(query.granularity)) {
return `Invalid granularity: ${query.granularity}`;
}
// Validate dates
if (query.startDate && !DateTime.fromISO(query.startDate as string).isValid) {
return `Invalid startDate: ${query.startDate}`;
}
if (query.endDate && !DateTime.fromISO(query.endDate as string).isValid) {
return `Invalid endDate: ${query.endDate}`;
}
return null;
}
// TODO: Validate the query params
const validationError = validateQueryParams(query);
if (validationError) {
throw createError({
statusCode: 400,
statusMessage: 'Invalid request parameters',
data: { message: validationError }
});
}
const filter: ActiveOrganizationsFilter = {

Comment on lines 46 to 47
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Important security concern documented.

This TODO comment correctly identifies a potential security risk. Passing unchecked query parameters directly to an external API could expose the application to injection attacks.

Consider implementing parameter validation or sanitization before passing values to Tinybird. For example:

function validateQueryParams(params: Record<string, any>): Record<string, string> {
  const validatedParams: Record<string, string> = {};
  
  // Validate and sanitize each parameter
  if (params.project && typeof params.project === 'string') {
    validatedParams.project = params.project.trim();
  }
  
  // Add similar validation for other parameters
  
  return validatedParams;
}

// Then use it before calling fetchFromTinybird
const validatedQuery = validateQueryParams(currentSummaryQuery);
fetchFromTinybird<TinybirdActiveContributorsSummary>('/v0/pipes/active_contributors.json', validatedQuery)

Signed-off-by: Raúl Santos <4837+borfast@users.noreply.github.com>
@borfast borfast force-pushed the active-organizations-widget branch from 2a00032 to 23a6844 Compare March 10, 2025 23:41
@borfast borfast merged commit b2eb5d8 into main Mar 10, 2025
2 checks passed
@borfast borfast deleted the active-organizations-widget branch March 10, 2025 23:42
@borfast borfast assigned borfast and unassigned borfast May 2, 2025
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.

2 participants