Skip to content

Conversation

ae2079
Copy link
Collaborator

@ae2079 ae2079 commented Apr 15, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new "Season" entity with start and end dates, linking projects, rounds, and user records to specific seasons.
    • Added repository functions for creating, retrieving, and managing seasons, including active season detection.
  • Refactor

    • Replaced all "seasonNumber" fields with foreign key relationships to the "Season" entity.
    • Updated queries, mutations, and services to use "seasonId" instead of "seasonNumber".
    • Enforced association of new projects with an active season during creation.
  • Tests

    • Updated tests to incorporate the "Season" entity and its relationships.
    • Added new tests covering season repository operations and active season logic.
  • Style

    • Reformatted configuration files to improve readability.

Copy link

coderabbitai bot commented Apr 15, 2025

Walkthrough

This update introduces a new Season entity and database table, replacing all previous usages of the primitive seasonNumber integer with a proper foreign key relationship to the Season entity across related tables and entities. All relevant TypeORM entities, GraphQL schemas, and repository functions are refactored to use seasonId and the Season relation instead of seasonNumber. The migration script creates the new table, updates columns, constraints, and indexes, and establishes new foreign key relationships. Test suites and service layers are updated to reflect this new association, ensuring all logic now references seasons via entity relationships rather than numeric identifiers.

Changes

File(s) Change Summary
migration/1744677815994-addSeasonsTable.ts Adds migration creating the season table, renames seasonNumber to seasonId in multiple tables, updates constraints and indexes, and establishes foreign key relationships.
src/entities/season.ts Introduces the new Season entity with fields, relations, and GraphQL decorators.
src/entities/entities.ts Adds Season to the exported entity list for the data source.
src/entities/project.ts
src/entities/projectUserRecord.ts
src/entities/qfRound.ts
src/entities/earlyAccessRound.ts
Refactors entities to replace seasonNumber with a season relation and seasonId foreign key; updates GraphQL fields and indexes accordingly.
src/repositories/seasonRepository.ts Adds repository functions for creating, finding, and listing Season entities.
src/repositories/projectUserRecordRepository.ts
src/services/qAccService.ts
Refactors all usages of seasonNumber to seasonId in function parameters, queries, and logic.
src/resolvers/donationResolver.ts
src/resolvers/qAccResolver.ts
Updates resolver logic to use seasonId and look up Season entities by number when needed.
src/repositories/seasonRepository.test.ts Adds new test suite for Season repository functions.
src/repositories/projectUserRecordRepository.test.ts
src/repositories/earlyAccessRoundRepository.test.ts
src/repositories/qfRoundRepository.test.ts
src/resolvers/donationResolver.test.ts
src/resolvers/qAccResolver.test.ts
src/resolvers/roundsResolver.test.ts
src/services/qAccService.test.ts
Refactors test setup and assertions to use Season entities and seasonId instead of seasonNumber.
package.json Reformats JSON for readability; no content changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Resolver
    participant Repository
    participant SeasonEntity
    participant RelatedEntity

    Client->>Resolver: Request (with seasonNumber)
    Resolver->>Repository: findSeasonByNumber(seasonNumber)
    Repository->>SeasonEntity: Query by seasonNumber
    SeasonEntity-->>Repository: Season (with id)
    Repository-->>Resolver: Season (with id)
    Resolver->>Repository: Operation using seasonId (from Season)
    Repository->>RelatedEntity: Query/Update with seasonId
    RelatedEntity-->>Repository: Result
    Repository-->>Resolver: Result
    Resolver-->>Client: Response
Loading

Suggested reviewers

  • aminlatifi
  • lovelgeorge99

Poem

In the warren of code, a new Season is born,
With tables and keys, the old ways are shorn.
No more mere numbers, but entities true,
Linking our projects to seasons anew.
Hopping through tests, relations now bloom—
The database garden has plenty of room!
🐇🌱

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1082af1 and f64b2ec.

📒 Files selected for processing (3)
  • migration/1744677815994-addSeasonsTable.ts (1 hunks)
  • src/resolvers/projectResolver.ts (3 hunks)
  • src/utils/errorMessages.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/utils/errorMessages.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • migration/1744677815994-addSeasonsTable.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/resolvers/projectResolver.ts (2)
src/repositories/seasonRepository.ts (1)
  • findActiveSeasonByDate (18-27)
src/utils/errorMessages.ts (2)
  • i18n (4-4)
  • translationErrorMessagesKeys (191-348)
🔇 Additional comments (3)
src/resolvers/projectResolver.ts (3)

128-128: LGTM: Import for the new season repository function

This import is necessary for finding the active season when creating a project, aligning with the PR objectives to use proper Season entities.


1384-1388: Appropriate error handling when no active season exists

Good implementation of checking for an active season and throwing a localized error message when none is found. This ensures projects are always associated with a valid season.


1449-1450: Correct assignment of Season entity relationship

The project now correctly stores both the full Season entity reference and the seasonId foreign key, which aligns with the PR objective of replacing primitive seasonNumber with proper entity relationships.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 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.

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

🔭 Outside diff range comments (3)
src/repositories/projectUserRecordRepository.test.ts (1)

302-365: ⚠️ Potential issue

Inconsistency: Test appears to use same seasonId when testing different seasons.

This test claims to test "reset individual caps for each season", but both "Season 1" and "Season 2" tests are using the same seasonId (season.id). If testing different seasons, you should create a second Season entity with a different seasonNumber.

 // Season 2 donations - should start fresh
 const season2EaRound = await saveEARoundDirectlyToDb({
   roundNumber: generateEARoundNumber(),
   startDate: new Date('2024-10-01'),
   endDate: new Date('2024-10-05'),
-  seasonId: season.id,
+  seasonId: (await Season.create({
+    seasonNumber: 2,
+    startDate: moment().subtract(1, 'month').toDate(),
+    endDate: moment().add(1, 'month').toDate(),
+  }).save()).id,
 });

The same issue exists with the season record queries at lines 349 and 359.

src/repositories/earlyAccessRoundRepository.test.ts (1)

101-139: ⚠️ Potential issue

Check date creation in active round test.

In the active round test, there's a potential issue with how dates are created:

startDate: new Date(new Date().setDate(new Date().getDate() - 1)), // yesterday
endDate: new Date(new Date().setDate(new Date().getDate() + 1)), // tomorrow

And for the inactive round:

startDate: new Date(new Date().getDate() + 1),
endDate: new Date(new Date().getDate() + 2),

The inactive round is using new Date(number) which creates a date from a timestamp. new Date().getDate() returns the day of month (1-31), which is likely not the intended behavior.

 const inactiveRoundData = {
   roundNumber: generateEARoundNumber(),
   seasonId: season.id,
-  startDate: new Date(new Date().getDate() + 1),
-  endDate: new Date(new Date().getDate() + 2),
+  startDate: new Date(new Date().setDate(new Date().getDate() + 1)),
+  endDate: new Date(new Date().setDate(new Date().getDate() + 2)),
   roundPOLCapPerProject: 1000000,
   roundPOLCapPerUserPerProject: 50000,
 };
src/services/qAccService.ts (1)

28-41: 💡 Verification agent

🧩 Analysis chain

Consider adding foreign key validation.

The seasonId parameter is accepted without validation. Consider adding a check to verify that the provided seasonId exists in the database to prevent potential runtime errors with invalid foreign keys.

// Add this at the beginning of getUserProjectSeasonRecord
if (seasonId) {
  const seasonExists = await Season.findOne({ where: { id: seasonId } });
  if (!seasonExists) {
    throw new Error(`Season with ID ${seasonId} not found`);
  }
}

This suggestion is based on a general best practice for foreign key validation. Let's check if there's any existing pattern for validation in the codebase:


🏁 Script executed:

#!/bin/bash
# Check for similar parameter validation patterns in the codebase
rg -A 2 'if \(\w+Id\)' --type ts

Length of output: 8387


Action Required: Add Foreign Key Validation for seasonId

We’ve observed that the getUserProjectSeasonRecord function in src/services/qAccService.ts accepts a seasonId parameter without checking its validity in the database. While similar conditional checks (e.g., if (seasonId)) exist elsewhere (as seen in src/repositories/projectUserRecordRepository.ts), they only adjust query conditions rather than verify that the related foreign record exists.

To prevent possible runtime errors due to an invalid seasonId, please add a foreign key validation at the beginning of the function. For instance, consider inserting:

if (seasonId) {
  const seasonExists = await Season.findOne({ where: { id: seasonId } });
  if (!seasonExists) {
    throw new Error(`Season with ID ${seasonId} not found`);
  }
}

This change makes sure that only a valid seasonId is used, aligning with best practices for ensuring data integrity in the codebase.

🧹 Nitpick comments (10)
src/repositories/projectUserRecordRepository.test.ts (1)

45-46: Replace await Promise.all with individual awaits for related deletion operations.

You're correctly using await for the deletion operations in the afterEach hook, which prevents test interference. However, consider using Promise.all for parallel execution when deleting related entities:

 if (eaRound) {
-  await Donation.delete({ earlyAccessRoundId: eaRound.id });
-  await ProjectRoundRecord.delete({ earlyAccessRoundId: eaRound.id });
+  await Promise.all([
+    Donation.delete({ earlyAccessRoundId: eaRound.id }),
+    ProjectRoundRecord.delete({ earlyAccessRoundId: eaRound.id })
+  ]);
   await eaRound.remove();
   eaRound = null;
 }

Also applies to: 111-162

src/entities/earlyAccessRound.ts (1)

92-94: Query filter updated correctly for season relationship.

The query filter in calculateCumulativeCaps has been properly updated to filter by seasonId instead of seasonNumber.

Consider updating the cache key to include seasonId:

-  .cache('cumulativeCapEarlyAccessRound-' + this.roundNumber, 300000)
+  .cache('cumulativeCapEarlyAccessRound-' + this.seasonId + '-' + this.roundNumber, 300000)

This would prevent cache collisions if different seasons have rounds with the same number.

src/repositories/seasonRepository.test.ts (2)

72-99: Active season test handles date conditions correctly.

The test creates past, current, and future seasons and correctly verifies that only the current season is returned as active.

Consider specifying a fixed date for testing instead of using moment() without parameters:

- const foundActiveSeason = await findActiveSeasonByDate();
+ const testDate = moment().toDate();
+ const foundActiveSeason = await findActiveSeasonByDate(testDate);

This would make the test more deterministic and less dependent on the execution time.


1-147: Consider adding additional test cases.

While the test suite covers the basic functionality well, consider adding tests for:

  1. Overlapping seasons handling
  2. Season date validation (startDate before endDate)
  3. Edge cases like seasons with same start/end dates
src/entities/qfRound.ts (1)

171-187: Query filter updated correctly for season relationship.

The calculateCumulativeCaps method has been properly updated to check for seasonId and filter by it in the query. This ensures proper isolation of data between seasons.

Consider updating the cache key to include seasonId:

-  .cache('cumulativeCapQfRound-' + this.roundNumber, 300000)
+  .cache('cumulativeCapQfRound-' + this.seasonId + '-' + this.roundNumber, 300000)

This would prevent cache collisions if different seasons have rounds with the same number.

src/entities/season.ts (2)

25-32: Date fields are properly defined.

The startDate and endDate fields are correctly configured for both database storage and GraphQL exposure.

Consider adding validation logic to ensure startDate is before endDate, either through a check constraint or an entity validation method.


1-48: Consider adding useful helper methods to the Season entity.

The Season entity could benefit from additional helper methods, such as:

  1. A method to check if a given date falls within the season
  2. A method to calculate the season's duration

Here's an example implementation:

@Entity()
@ObjectType()
export class Season extends BaseEntity {
  // existing fields...
  
  // Helper method to check if a date falls within this season
  isDateInSeason(date: Date): boolean {
    return date >= this.startDate && date <= this.endDate;
  }
  
  // Helper method to get duration in days
  getDurationInDays(): number {
    return Math.ceil((this.endDate.getTime() - this.startDate.getTime()) / (1000 * 60 * 60 * 24));
  }
}
src/repositories/seasonRepository.ts (3)

4-8: Simple finder by ID is straightforward.
Returning null if not found is acceptable. Consider throwing an error or returning a typed result if a not-found state requires specialized handling.


10-16: Finder by season number is clear.
Again, consider validating the result or throwing a custom error if no record is found to reduce null checks in downstream code.


37-49: Season creation logic looks solid.
Parameter destructuring and direct save are straightforward. Consider validating conflicts, e.g., overlapping date ranges with other seasons.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9e36228 and 1082af1.

📒 Files selected for processing (21)
  • migration/1744677815994-addSeasonsTable.ts (1 hunks)
  • package.json (1 hunks)
  • src/entities/earlyAccessRound.ts (3 hunks)
  • src/entities/entities.ts (2 hunks)
  • src/entities/project.ts (2 hunks)
  • src/entities/projectUserRecord.ts (2 hunks)
  • src/entities/qfRound.ts (5 hunks)
  • src/entities/season.ts (1 hunks)
  • src/repositories/earlyAccessRoundRepository.test.ts (10 hunks)
  • src/repositories/projectUserRecordRepository.test.ts (28 hunks)
  • src/repositories/projectUserRecordRepository.ts (4 hunks)
  • src/repositories/qfRoundRepository.test.ts (11 hunks)
  • src/repositories/seasonRepository.test.ts (1 hunks)
  • src/repositories/seasonRepository.ts (1 hunks)
  • src/resolvers/donationResolver.test.ts (2 hunks)
  • src/resolvers/donationResolver.ts (1 hunks)
  • src/resolvers/qAccResolver.test.ts (4 hunks)
  • src/resolvers/qAccResolver.ts (2 hunks)
  • src/resolvers/roundsResolver.test.ts (16 hunks)
  • src/services/qAccService.test.ts (8 hunks)
  • src/services/qAccService.ts (5 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (9)
src/resolvers/qAccResolver.test.ts (1)
test/testUtils.ts (1)
  • generateEARoundNumber (2080-2082)
src/services/qAccService.test.ts (1)
test/testUtils.ts (1)
  • generateEARoundNumber (2080-2082)
src/repositories/seasonRepository.test.ts (1)
src/repositories/seasonRepository.ts (5)
  • createSeason (37-49)
  • findSeasonById (4-8)
  • findSeasonByNumber (10-16)
  • findActiveSeasonByDate (18-27)
  • findAllSeasons (29-35)
src/services/qAccService.ts (2)
src/repositories/projectUserRecordRepository.ts (1)
  • updateOrCreateProjectUserRecord (4-64)
src/resolvers/roundsResolver.ts (1)
  • activeRound (57-69)
src/repositories/earlyAccessRoundRepository.test.ts (1)
test/testUtils.ts (2)
  • generateEARoundNumber (2080-2082)
  • saveEARoundDirectlyToDb (2068-2073)
src/resolvers/roundsResolver.test.ts (1)
test/testUtils.ts (1)
  • generateEARoundNumber (2080-2082)
src/resolvers/qAccResolver.ts (2)
src/repositories/seasonRepository.ts (1)
  • findSeasonByNumber (10-16)
src/repositories/projectUserRecordRepository.ts (1)
  • getProjectUserRecordAmount (71-90)
src/resolvers/donationResolver.test.ts (1)
test/testUtils.ts (1)
  • generateEARoundNumber (2080-2082)
src/entities/projectUserRecord.ts (1)
src/entities/season.ts (1)
  • Entity (14-48)
🔇 Additional comments (62)
package.json (1)

1-248: Formatting improvement only—no functional changes.

This change only improves readability and does not affect functionality. No further action required.

src/entities/entities.ts (2)

42-42: Season entity import is correct and necessary.

The import of the Season entity is required for its registration and use in the ORM.


96-97: Season entity registration is correct.

Adding Season to the entities array ensures it is managed by TypeORM and available for migrations and queries.

src/resolvers/donationResolver.ts (1)

999-1001: Correct migration to seasonId.

The update from seasonNumber to seasonId and the new value source is correct and aligns with the new relational model.

src/resolvers/donationResolver.test.ts (3)

71-71: Good addition of Season import

This import brings in the Season entity which is now needed for the test setup. It aligns with the broader schema changes replacing primitive seasonNumber fields with proper relations.


5087-5092: Proper Season setup for tests

Good implementation of clearing existing Season records and creating a test Season entity with appropriate date ranges before testing. This ensures the test environment is properly initialized with the new Season entity that early access rounds now depend on.


5095-5095: Correct usage of seasonId in EarlyAccessRound

The EarlyAccessRound now correctly references the Season entity via seasonId instead of using the previous seasonNumber field. This aligns with the database schema changes.

src/resolvers/qAccResolver.test.ts (5)

39-39: Good addition of Season import

Adding the Season import is necessary to support the new entity relationship model where rounds are associated with a Season entity instead of using a primitive seasonNumber field.


152-152: Appropriate Season class variable declaration

Adding a class-level Season variable helps maintain consistency by providing a single Season instance that can be referenced by all test rounds.


155-159: Well-implemented Season initialization

The Season setup is properly implemented with appropriate seasonNumber and date ranges. This initialization is essential for the test setup since EarlyAccessRound and QfRound entities now depend on a valid Season entity.


168-168: Consistent Season association across all EarlyAccessRounds

Each EarlyAccessRound entity now correctly references the Season entity through seasonId. This is consistent with the database schema changes and ensures proper relationships are maintained.

Also applies to: 176-176, 184-184, 192-192


202-202: Proper Season association for QfRound

The QfRound entity now correctly references the Season entity through seasonId, aligning with the database schema changes and maintaining consistency with EarlyAccessRound associations.

src/resolvers/qAccResolver.ts (2)

16-16: Good addition of Season repository import

The findSeasonByNumber import is necessary to support the translation from seasonNumber to seasonId in the resolver.


69-81: Well-implemented seasonNumber to seasonId translation

The implementation properly translates the optional seasonNumber parameter to a seasonId by looking up the corresponding Season entity. It includes appropriate error handling when a Season is not found.

This change maintains backward compatibility by keeping the API signature unchanged while adapting the internal implementation to work with the new relational model.

src/resolvers/roundsResolver.test.ts (5)

19-19: Correctly importing the new Season entity

The import of the new Season entity is properly added which is necessary for the subsequent changes in the test file.


24-24: Season integration in fetchAllRoundsTestCases is well implemented

The test setup now properly creates a Season instance before creating Early Access Rounds and QF Rounds, and cleans up Season data during both setup and teardown phases. This ensures test isolation and provides the necessary test fixture for the relationships established in the refactored entities.

Also applies to: 39-44


68-69: Correctly updated seasonNumber to seasonId in round creation

All references to the previously used seasonNumber: 1 in the creation of EarlyAccessRound and QfRound entities have been properly replaced with seasonId: season.id, explicitly linking rounds to the created Season entity.

Also applies to: 77-78, 89-90, 102-103


180-200: Season integration in fetchActiveRoundTestCases is properly implemented

Similar to the first test case, the fetchActiveRoundTestCases function now correctly creates and manages a Season instance. The test setup follows the same pattern of creating a Season before rounds, and cleaning up all Season data after tests complete.


224-229: Consistent application of seasonId across all test cases

The Season relationship is consistently applied across all test cases in the file. Every instance of round creation now references the Season entity via its ID, maintaining data consistency and relationship integrity.

Also applies to: 236-244, 269-272, 279-287, 311-319, 338-346, 363-366, 373-380

src/repositories/qfRoundRepository.test.ts (4)

27-27: Correctly importing the new Season entity

The import of the Season entity is properly added, which is necessary for the subsequent changes in the test file.


466-467: Minor formatting improvement

A blank line was added to improve code readability, which is a good practice to separate logical code blocks.


504-524: Season entity properly integrated in QF Round tests

The test setup now correctly creates a Season instance before creating QF Rounds and properly cleans up Season data during both setup and teardown phases. This ensures test isolation and provides the necessary fixtures for the entity relationships.


568-569: Consistently replaced seasonNumber with seasonId in QF Round creation

All references to the previously used seasonNumber field in creating QfRound entities have been properly replaced with seasonId: season.id, explicitly linking rounds to the created Season entity.

Also applies to: 581-582, 594-595, 620-621, 633-634, 645-646

src/services/qAccService.test.ts (4)

23-23: Correctly importing and cleaning up the new Season entity

The import of the Season entity is properly added, and the test setup now cleans up Season data in the before hook, which is essential for test isolation.

Also applies to: 30-30


37-38: Well-structured Season setup for different test scenarios

The test now creates two Season instances with different season numbers, allowing tests to verify behavior across different seasons. The setup defines proper date ranges for each season, which is good for comprehensive test coverage.

Also applies to: 61-71


77-78: Early Access Rounds and QF Rounds properly linked to seasons

All references to seasonNumber in the creation of EarlyAccessRound and QfRound entities have been correctly replaced with seasonId: season.id, explicitly linking rounds to the created Season entity.

Also applies to: 85-86, 93-94, 101-102, 114-115, 128-129


551-552: Consistent usage of both seasons in advanced test cases

The test properly uses both defined seasons (season and season2) in different test scenarios, providing good test coverage for cross-season behaviors. This is especially valuable for testing the cumulative donation caps across seasons.

Also applies to: 687-688, 805-806

src/entities/project.ts (2)

50-50: Correctly importing the new Season entity

The import of the Season entity is properly added, which is necessary for establishing the entity relationship.


500-507: Well-structured Season relationship in Project entity

The changes properly implement:

  1. A GraphQL field exposing the Season entity
  2. A many-to-one relationship between Project and Season
  3. A seasonId column with the RelationId decorator

This is a well-structured implementation following TypeORM and type-graphql best practices, replacing the primitive seasonNumber field with a proper entity relationship.

src/repositories/projectUserRecordRepository.test.ts (1)

29-59: Proper setup and cleanup for Season entity.

Good implementation of the Season entity in the test setup and cleanup. Creating the Season in the beforeEach and properly cleaning it up in afterEach ensures test isolation.

src/repositories/earlyAccessRoundRepository.test.ts (2)

3-14: Proper import of moment and Season entity.

Good addition of the moment library for date handling and the Season entity. The moment library provides a clean way to define relative dates for season start and end.


20-29: Well-structured test setup with proper cleanup.

Good implementation of test setup that cleans existing data and creates a Season entity with appropriate dates. This ensures each test starts with a clean state.

src/services/qAccService.ts (2)

72-107: Good parameter and type updates for seasonId.

You've correctly updated the getUserProjectSeasonRecord function to use seasonId instead of seasonNumber, including the function signature, type definitions, and query parameters.


179-183: Consistent seasonId usage across all function calls.

All function calls to getUserProjectSeasonRecord have been updated to use seasonId from the respective round entity, maintaining consistency with the new Season model.

Also applies to: 202-206, 270-273

src/repositories/projectUserRecordRepository.ts (3)

4-12: Updated function signature with seasonId parameter.

The function signature has been properly updated to use seasonId instead of seasonNumber, maintaining the optional nature of the parameter.


13-50: SQL query updates for seasonId use in joins and conflict resolution.

The SQL query has been properly updated to:

  1. Use seasonId in the INSERT statement
  2. Join with qf_round and early_access_round tables using seasonId
  3. Use seasonId in the ON CONFLICT clause

These changes correctly implement the Season entity relationship in the database queries.


71-84: Updated getProjectUserRecordAmount function with seasonId parameter.

The getProjectUserRecordAmount function has been properly updated to use seasonId in both its signature and database query.

src/entities/earlyAccessRound.ts (3)

11-16: Dependencies correctly updated to support the new Season relationship.

The necessary imports for TypeORM's relationship decorators and the new Season entity have been added appropriately.


30-36: Season relationship properly implemented.

The ManyToOne relationship to the Season entity is correctly established, replacing the previous primitive seasonNumber field. The GraphQL field is properly exposed with the correct type.


34-36: RelationId mapping is correctly configured.

The RelationId decorator properly maps the seasonId column from the season relationship, maintaining database integrity.

src/repositories/seasonRepository.test.ts (6)

1-11: Test dependencies and imports are correctly set up.

All necessary imports for testing the Season repository functionality are properly included.


12-21: Clean testing environment with proper setup and teardown.

The test suite correctly cleans up data before and after each test to ensure test isolation.


23-42: Create season test is well-implemented.

The test correctly verifies the creation of a new season with proper assertions for all fields.


44-56: Find by ID and number tests are correctly implemented.

Both tests properly verify the functionality to find seasons by their unique identifiers.

Also applies to: 58-70


101-127: Order of seasons is correctly tested.

The test properly verifies that seasons are returned in descending order by seasonNumber.


129-146: Null case for active season is properly tested.

The test correctly handles the scenario where no active season exists for the current date.

src/entities/qfRound.ts (3)

13-18: Dependencies correctly updated to support the new Season relationship.

The necessary imports for TypeORM's relationship decorators and the new Season entity have been added appropriately.


32-38: Season relationship properly implemented.

The ManyToOne relationship to the Season entity is correctly established, replacing the previous primitive seasonNumber field. The GraphQL field is properly exposed with the correct type.


197-202: Fallback logic for rounds without season is preserved.

The fallback logic for rounds without a season association has been maintained and the comment was updated to reflect the new terminology.

src/entities/season.ts (4)

1-13: New Season entity imports are correct.

All necessary imports for TypeORM entity definition and GraphQL type generation are included.


33-39: Relationship to Project entities is properly defined.

The one-to-many relationship to Project entities is correctly configured with bidirectional mapping.


37-40: Relationship to ProjectUserRecord entities is properly defined.

The one-to-many relationship to ProjectUserRecord entities is correctly configured with bidirectional mapping.


41-47: Timestamp fields are properly configured.

The createdAt and updatedAt timestamp fields are correctly set up with TypeORM decorators to handle automatic updates.

src/entities/projectUserRecord.ts (3)

13-13: Import of Season entity looks appropriate.
No issues found; this aligns with the new many-to-one relation.


17-17: Verify uniqueness handling for nullable seasonId.
In PostgreSQL, multiple NULL values do not violate a unique index, so it’s possible for many records to share a NULL seasonId. Ensure this behavior is desired (i.e., multiple records without a season are allowed).


55-62: Relation to Season entity is consistent with domain model.
Because seasonId is nullable, confirm that existing records can remain seasonless or that the application logic gracefully handles records without a season. If this matches the business rules, it looks good.

src/repositories/seasonRepository.ts (3)

1-2: Imports are adequately scoped.
Using LessThanOrEqual and MoreThanOrEqual from TypeORM is fine for date range checks. No concerns.


18-27: Date-based active season retrieval is well-structured.
The usage of LessThanOrEqual(date) and MoreThanOrEqual(date) is correct for inclusive range checks.


29-35: Fetching all seasons in descending order is correct.
This helps retrieve the most recent season first. No issues found.

migration/1744677815994-addSeasonsTable.ts (3)

1-5: Migration class name and structure are appropriate.
No immediate concerns.


6-52: Up migration correctly creates the seasons table and updates foreign keys.
Make sure there is a plan for existing data referencing seasonNumber. If needed, a data-migration step that maps those old values to newly created Season rows should be included so legacy records aren’t orphaned.


54-99: Down migration properly reverses schema changes.
This is consistent with TypeORM’s convention, ensuring a clean rollback if necessary.

Comment on lines +28 to 41
if (seasonId) {
query += ` AND (
(donation."qfRoundId" IS NOT NULL AND EXISTS (
SELECT 1 FROM qf_round
WHERE qf_round.id = donation."qfRoundId"
AND qf_round."seasonNumber" = $3
AND qf_round."seasonId" = $3
)) OR
(donation."earlyAccessRoundId" IS NOT NULL AND EXISTS (
SELECT 1 FROM early_access_round
WHERE early_access_round.id = donation."earlyAccessRoundId"
AND early_access_round."seasonNumber" = $3
AND early_access_round."seasonId" = $3
))
)`;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Consider adding transaction support for data consistency.

The function performs a complex INSERT with ON CONFLICT resolution, but it's not wrapped in a transaction. Consider adding transaction support to ensure data consistency, especially when this function is called in high-concurrency scenarios.

 export async function updateOrCreateProjectUserRecord({
   projectId,
   userId,
   seasonId,
 }: {
   projectId: number;
   userId: number;
   seasonId?: number;
 }): Promise<ProjectUserRecord> {
+  // Use a transaction to ensure data consistency
+  const queryRunner = ProjectUserRecord.getRepository().manager.connection.createQueryRunner();
+  await queryRunner.connect();
+  await queryRunner.startTransaction();
+  
+  try {
     let query = `
     INSERT INTO project_user_record ("projectId", "userId", "seasonId", "eaTotalDonationAmount", "qfTotalDonationAmount", "totalDonationAmount")
     // ... rest of the query ...
     `;
     
     // ... query construction ...
     
-    const result = await ProjectUserRecord.query(query, [
+    const result = await queryRunner.query(query, [
       projectId,
       userId,
       seasonId || null,
       [
         DONATION_STATUS.VERIFIED,
         DONATION_STATUS.PENDING,
         DONATION_STATUS.SWAP_PENDING,
       ],
     ]);
 
+    await queryRunner.commitTransaction();
     return result[0];
+  } catch (error) {
+    await queryRunner.rollbackTransaction();
+    throw error;
+  } finally {
+    await queryRunner.release();
+  }
 }

Let's check if transactions are used elsewhere in the codebase for similar operations:


🏁 Script executed:

#!/bin/bash
# Check for transaction patterns in the repository
rg -A 2 'startTransaction|createQueryRunner' --type ts

Length of output: 1785


Transaction Handling: Ensure Atomicity in Project User Record Updates

The current implementation of the INSERT with ON CONFLICT resolution in src/repositories/projectUserRecordRepository.ts does not use transaction support, which may lead to data inconsistencies under high concurrency. Similar transaction patterns from other parts of the codebase (e.g., in src/resolvers/reactionResolver.ts and test scripts) indicate the preferred approach for managing complex operations.

  • Wrap the INSERT operation in a transaction to guarantee atomicity.
  • Use a query runner to start the transaction, execute the query, and then either commit or roll back based on the outcome.
  • Consider aligning the transaction creation method with that used elsewhere (i.e., using AppDataSource.getDataSource().createQueryRunner() if applicable) for consistency.

Below is an updated diff snippet that wraps the query within a transaction block:

 export async function updateOrCreateProjectUserRecord({
   projectId,
   userId,
   seasonId,
 }: {
   projectId: number;
   userId: number;
   seasonId?: number;
 }): Promise<ProjectUserRecord> {
-  const result = await ProjectUserRecord.query(query, [
+  // Use a transaction to ensure data consistency
+  const queryRunner = ProjectUserRecord.getRepository().manager.connection.createQueryRunner();
+  await queryRunner.connect();
+  await queryRunner.startTransaction();
+
+  try {
+    const result = await queryRunner.query(query, [
       projectId,
       userId,
       seasonId || null,
       [
         DONATION_STATUS.VERIFIED,
         DONATION_STATUS.PENDING,
         DONATION_STATUS.SWAP_PENDING,
       ],
     ]);
+
+    await queryRunner.commitTransaction();
+    return result[0];
+  } catch (error) {
+    await queryRunner.rollbackTransaction();
+    throw error;
+  } finally {
+    await queryRunner.release();
+  }
 }

Please adjust the transaction initialization as needed to match the repository’s standard (for example, using AppDataSource.getDataSource().createQueryRunner() if that’s the consistent pattern elsewhere).

Comment on lines +14 to +24
@Entity()
@ObjectType()
export class Season extends BaseEntity {
@Field(_type => ID)
@PrimaryGeneratedColumn()
id: number;

@Field(_type => Int)
@Column()
seasonNumber: number;

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Season entity basic structure is well-defined.

The Season entity is properly configured with appropriate decorators for both TypeORM and GraphQL. The primary key and seasonNumber fields are correctly defined.

Add a unique constraint to the seasonNumber field to prevent duplicate season numbers:

  @Field(_type => Int)
  @Column()
+ @Index({ unique: true })
  seasonNumber: number;
📝 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
@Entity()
@ObjectType()
export class Season extends BaseEntity {
@Field(_type => ID)
@PrimaryGeneratedColumn()
id: number;
@Field(_type => Int)
@Column()
seasonNumber: number;
@Entity()
@ObjectType()
export class Season extends BaseEntity {
@Field(_type => ID)
@PrimaryGeneratedColumn()
id: number;
@Field(_type => Int)
@Column()
@Index({ unique: true })
seasonNumber: number;
}

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.

1 participant