Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Clean architecture entities, controllers and services #132

Merged
merged 6 commits into from
Dec 21, 2024

Conversation

yeahlowflicker
Copy link
Collaborator

@yeahlowflicker yeahlowflicker commented Dec 14, 2024

引入 Clean Architecture 概念,包含 Entity, Controller 和 Service:

  • Entity: 資料模型,以物件方式運作
  • Controller: 後端控制器,負責與 Supabase 溝通
  • Service: 負責資料轉換、檢查

每個 Entity 內會多含一個 DBXXXX 的 type,如 User 有 DBUser,後者是 Supabase 產生的 type,會透過 Service 轉成正常的 entity type。

ErrorHandler 是通用的錯誤處理器,供後續作 error display、狀態更新使用。

其餘細節、用法請參閱各 function 註解。

(2024/12/14)

Summary by CodeRabbit

  • New Features

    • Introduced a universal error handler for improved error management.
    • Added functionality to retrieve and find events and users from the database.
    • Implemented user identity conversion for better user representation.
  • Bug Fixes

    • Enhanced error handling during data retrieval operations.
  • Documentation

    • Added documentation comments for new classes and methods to improve clarity.

This commit implements the clean architecture concept.
The "features" folder has been introduced, containing
controllers, entities, and services for different modules.

Currently the event and user modules have been implemented with
basic fetch functions (get all and find by ID).

The ErrorHandler class is introduced for universal error handling.

- Added User, UserController and UserService
- Added Event, EventController and EventService
- Added ErrorHandler

(2024/12/14)
Copy link
Contributor

coderabbitai bot commented Dec 14, 2024

Walkthrough

A comprehensive set of changes has been introduced to enhance the backend infrastructure for user and event management. The changes include the creation of new utility classes, controllers, entities, and services across multiple files. A new ErrorHandler utility provides centralized error management for Supabase interactions. Separate controllers for users and events have been developed to handle data retrieval, with corresponding entity and service classes to manage data transformation and type conversion.

Changes

File Change Summary
src/utils/ErrorHandler.ts Added ErrorHandler class with static handleSupabaseError method for centralized Supabase error handling
src/backend/event/Controllers/EventController.ts Introduced EventController with methods getEvents and findEventByID for event data retrieval
src/backend/event/Entities/Event.ts Added DBEvent type alias and Event class to represent event data structures
src/backend/event/Services/EventService.ts Created EventService with parseEvent method to convert database events to application events
src/backend/user/Controllers/UserController.ts Implemented UserController with getUsers and findUserByID methods for user data management
src/backend/user/Entities/User.ts Defined DBUser type and User class with identity conversion method
src/backend/user/Services/UserService.ts Added UserService with parseUser method to transform database user records

Poem

🐰 In the realm of code, a new dawn breaks,
Controllers dance, services awake!
Error handlers guard with gentle might,
Users and events now shine so bright 🌟
A rabbit's code, precise and clean,
Bringing structure to the unseen! 🚀

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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. (Beta)
  • @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
Contributor

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

🧹 Outside diff range and nitpick comments (7)
src/features/user/Controllers/UserController.ts (2)

16-18: Correct type annotation in documentation

In the JSDoc example, use Array<User> instead of Array<Users> to match the correct type name.

Apply this diff to fix the documentation:

  * @usage userController.getUsers(<PARAMS>).then(
- *          (users: Array<Users>) => { ... }
+ *          (users: Array<User>) => { ... }
  *        )

76-80: Correct documentation to reference 'user' instead of 'event'

The JSDoc comments for findUserByID incorrectly reference eventController and Event instead of userController and User. This appears to be a copy-paste error.

Apply this diff to fix the documentation:

  /**
   * Find a single user by ID
   * 
-  * @usage eventController.FindEventByID(<PARAMS>).then(
-  *          (event: Event) => { ... }
+  * @usage userController.findUserByID(<PARAMS>).then(
+  *          (user: User) => { ... }
   *        )
src/features/event/Controllers/EventController.ts (2)

18-18: Correct type annotation in documentation

In the JSDoc example, use Array<Event> instead of Array<Events> to match the correct type name.

Apply this diff to fix the documentation:

  * @usage eventController.getEvents(<PARAMS>).then(
- *          (events: Array<Events>) => { ... }
+ *          (events: Array<Event>) => { ... }
  *        )

80-80: Correct method name casing in documentation

The method name in the JSDoc usage example should be findEventByID with a lowercase 'f' to match the actual method name.

Apply this diff to fix the documentation:

  * @usage eventController.FindEventByID(<PARAMS>).then(
+ * @usage eventController.findEventByID(<PARAMS>).then(
src/utils/ErrorHandler.ts (1)

11-17: Refactor 'ErrorHandler' to use simple functions instead of a static-only class

The ErrorHandler class contains only static members, which can be simplified by exporting functions directly. This reduces complexity and aligns with best practices.

Apply this diff to refactor the code:

 import { PostgrestError } from "@supabase/supabase-js";

-/**
- * A universal error handler class.
- * 
- * This will be called by all controllers and is useful for general
- * error-handling logic.
- * 
- * @author Henry 
- */
-export default class ErrorHandler {
-
-    public static handleSupabaseError(error: PostgrestError) {
-        console.error(error)
-    }
-
-}
+/**
+ * A universal error handler function.
+ * 
+ * This function is called by all controllers and is useful for general
+ * error-handling logic.
+ * 
+ * @author Henry 
+ */
+export function handleSupabaseError(error: PostgrestError) {
+    console.error(error)
+}

You will also need to update the imports in other files that use ErrorHandler:

- import ErrorHandler from "../../../utils/ErrorHandler";
+ import { handleSupabaseError } from "../../../utils/ErrorHandler";

And update the usage:

- ErrorHandler.handleSupabaseError(error)
+ handleSupabaseError(error)
🧰 Tools
🪛 Biome (1.9.4)

[error] 11-17: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/features/event/Entities/Event.ts (1)

9-22: Consider using an interface for the 'Event' data structure

Since the Event class only contains properties and no methods, it might be more appropriate to define it as an interface. This can enhance type safety and flexibility in your code.

Apply this diff to refactor Event into an interface:

 export default class Event {

-    public id:                  number  = 0;
-    public name:                string  = "";
-    public type:                number  = 0;
-    public description:         string  = "";
-    public startTime:           string  = "";
-    public endTime:             string  = "";
-    public location:            string  = "";
-    public fee:                 number  = 0;
-    public userID:              string  = "";
-    public createdAt:           string  = "";

+}

+export interface Event {
+    id:           number;
+    name:         string;
+    type:         number;
+    description:  string;
+    startTime:    string;
+    endTime:      string;
+    location:     string;
+    fee:          number;
+    userID:       string;
+    createdAt:    string;
 }

This change will require updating any code that instantiates Event objects using the new keyword, as interfaces cannot be instantiated.

src/features/event/Services/EventService.ts (1)

12-13: Consider using ISO date strings for time fields.

The startTime and endTime fields should use a consistent date format. Consider using ISO strings for better compatibility.

-    event.startTime = record.start_time ? record.start_time : ""
-    event.endTime = record.end_time ? record.end_time : ""
+    event.startTime = record.start_time ? new Date(record.start_time).toISOString() : ""
+    event.endTime = record.end_time ? new Date(record.end_time).toISOString() : ""

Also applies to: 13-14

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a1ff48f and 07f9fbd.

📒 Files selected for processing (7)
  • src/features/event/Controllers/EventController.ts (1 hunks)
  • src/features/event/Entities/Event.ts (1 hunks)
  • src/features/event/Services/EventService.ts (1 hunks)
  • src/features/user/Controllers/UserController.ts (1 hunks)
  • src/features/user/Entities/User.ts (1 hunks)
  • src/features/user/Services/UserService.ts (1 hunks)
  • src/utils/ErrorHandler.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/utils/ErrorHandler.ts

[error] 11-17: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/features/user/Services/UserService.ts

[error] 3-24: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/features/event/Services/EventService.ts

[error] 3-22: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🔇 Additional comments (2)
src/features/user/Controllers/UserController.ts (1)

46-47: ⚠️ Potential issue

Fix range conditional check to include zero values

The current condition if (rangeStart && rangeEnd) will fail when rangeStart or rangeEnd is 0, because 0 is a falsy value in JavaScript/TypeScript. To allow ranges starting at 0, check for undefined instead.

Apply this diff to fix the issue:

 if (orderBy)
     query.order(orderBy, { ascending: !orderDescending })

-if (rangeStart && rangeEnd)
+if (rangeStart !== undefined && rangeEnd !== undefined)
     query.range(rangeStart, rangeEnd)

Likely invalid or redundant comment.

src/features/event/Controllers/EventController.ts (1)

47-48: ⚠️ Potential issue

Fix range conditional check to include zero values

The condition if (rangeStart && rangeEnd) will fail when rangeStart or rangeEnd is 0. Modify the condition to check for undefined instead to allow ranges starting at 0.

Apply this diff to fix the issue:

 if (orderBy)
     query.order(orderBy, { ascending: !orderDescending })

-if (rangeStart && rangeEnd)
+if (rangeStart !== undefined && rangeEnd !== undefined)
     query.range(rangeStart, rangeEnd)

Likely invalid or redundant comment.

src/features/user/Controllers/UserController.ts Outdated Show resolved Hide resolved
src/features/event/Controllers/EventController.ts Outdated Show resolved Hide resolved
src/features/user/Entities/User.ts Outdated Show resolved Hide resolved
src/features/user/Entities/User.ts Outdated Show resolved Hide resolved
src/features/user/Entities/User.ts Outdated Show resolved Hide resolved
src/features/user/Services/UserService.ts Outdated Show resolved Hide resolved
src/features/user/Services/UserService.ts Outdated Show resolved Hide resolved
src/features/event/Services/EventService.ts Outdated Show resolved Hide resolved
Copy link
Member

@daviddaytw daviddaytw left a comment

Choose a reason for hiding this comment

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

@yeahlowflicker Fantastic work!

This PR is very important since it will solidify future code styles.

I'm wondering if Entities are really necessary. They seem redundant since we already have Supabase defining models. What are the benefits?
Also, what does features mean actually? Is there a better vocabulary for it?

@yeahlowflicker
Copy link
Collaborator Author

@daviddaytw Thank you David.

I introduced the entities because of the following 3 reasons:

  1. Ensure properties are not null by setting default values within the entities. This may be useful when fetching partial columns.

  2. Facilitate data transfer within the frontend by using unified types.

  3. Provide utility functions directly in the entity objects, like data validation methods and text mapping.

Regarding the folder name, what do you think about the term modules?

These are based on my limited understanding of the Supabase types. Please feel free to correct me if there's anything wrong, thanks.

@daviddaytw
Copy link
Member

I see the reasons. I'm okay with that if an internal consensus is made.
In my understanding, the folder will be everything related to the database or backend. Can we use either one as the name so it is self-explaining?

@1989ONCE 1989ONCE added structure refactor, db model design enhancement New feature or request labels Dec 15, 2024
@1989ONCE 1989ONCE self-requested a review December 15, 2024 09:25
1989ONCE
1989ONCE previously approved these changes Dec 15, 2024
@1989ONCE
Copy link
Member

@daviddaytw Thanks for the advice! I think backend could be a solid choice 🤩
@yeahlowflicker Does backend work for you, or do you have other ideas? Would love to hear your thoughts!

@yeahlowflicker
Copy link
Collaborator Author

@daviddaytw @1989ONCE
backend seems logical to me, thanks.

This renaming is made to prevent confusion.
Copy link
Contributor

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

🧹 Nitpick comments (6)
src/backend/event/Controllers/EventController.ts (1)

80-80: Correct the method name in usage example

The method name in the usage example should match the actual method findEventByID (all lowercase). The current example uses FindEventByID with an uppercase 'F'.

Suggested fix:

-     * @usage eventController.FindEventByID(<PARAMS>).then(
+     * @usage eventController.findEventByID(<PARAMS>).then(
src/backend/event/Services/EventService.ts (1)

3-22: Consider refactoring EventService to use a function instead of a static-only class

The EventService class currently contains only static methods. According to best practices, it's preferable to use simple functions instead of classes with only static members to reduce unnecessary complexity.

Suggested change:

- export default class EventService {
-     
-     public static parseEvent(record: DBEvent) : Event {
+ export function parseEvent(record: DBEvent) : Event {
          const event             = new Event()

          event.id                = record.id
          event.name              = record.name ?? ""
          event.type              = record.type ?? 0
          event.description       = record.description ?? ""
          event.startTime         = record.start_time ?? ""
          event.endTime           = record.end_time ?? ""
          event.location          = record.location ?? ""
          event.fee               = record.fee ?? 0
          event.userID            = record.user_id
          event.createdAt         = record.created_at

          return event
-     }
- }
🧰 Tools
🪛 Biome (1.9.4)

[error] 15-15: Expected an expression but instead found ':'.

Expected an expression here.

(parse)


[error] 3-22: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/backend/user/Controllers/UserController.ts (2)

95-96: Redundant .limit(1) before .single()

The .single() method already retrieves a single record and returns it as an object. Using .limit(1) is redundant.

Apply this diff to remove the unnecessary .limit(1):

.eq("uuid", userID)
.single()
-    .limit(1)

33-39: Provide default values for optional parameters

The optional parameters orderDescending, rangeStart, and rangeEnd do not have default values. This may lead to unexpected behavior if they are undefined.

Apply this diff to set default values:

-public async getUsers(
-    fields: string,
-    orderBy?: string,
-    orderDescending?: boolean,
-    rangeStart?: number,
-    rangeEnd?: number
-) : Promise<Array<User> | null> {
+public async getUsers(
+    fields: string,
+    orderBy?: string,
+    orderDescending: boolean = false,
+    rangeStart: number = 0,
+    rangeEnd: number = 100
+) : Promise<Array<User> | null> {
src/backend/user/Services/UserService.ts (1)

3-24: Refactor static-only class into a module with functions

The UserService class contains only a static method and no instance members. It's more conventional in TypeScript to use standalone functions or an object when only static members are present.

Apply this refactor to convert the class into a module with an exported function:

-export default class UserService {
-
-    /**
-     * Convert a user from default Supabase type to User entity
-     * 
-     * @param   {DBUser} record   - The record retrieved from Supabase
-     * @returns {User}            - Converted user entity
-     * 
-     * @author ...
-     */
-    public static parseUser(record: DBUser) : User {
-        const user          = new User()
-        user.id             = record.uuid
-        user.username       = record.name
-        user.email          = record.fk_email
-        user.identity       = record.fk_identity
-        user.avatar         = record.avatar
-
-        return user
-    }
-
-}
+/**
+ * Convert a user from default Supabase type to User entity
+ * 
+ * @param   {DBUser} record   - The record retrieved from Supabase
+ * @returns {User}            - Converted user entity
+ * 
+ */
+export function parseUser(record: DBUser) : User {
+    const user          = new User()
+    user.id             = record.uuid
+    user.username       = record.name
+    user.email          = record.fk_email
+    user.identity       = record.fk_identity
+    user.avatar         = record.avatar
+
+    return user
+}
🧰 Tools
🪛 Biome (1.9.4)

[error] 3-24: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/backend/user/Entities/User.ts (1)

21-21: Reconsider default value for joinedAt property

Initializing joinedAt with new Date() sets it to the current date, which may not reflect the user's actual join date from the database. Consider setting the default value to null or handling the assignment during data parsing.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 07f9fbd and 4dc1147.

📒 Files selected for processing (6)
  • src/backend/event/Controllers/EventController.ts (1 hunks)
  • src/backend/event/Entities/Event.ts (1 hunks)
  • src/backend/event/Services/EventService.ts (1 hunks)
  • src/backend/user/Controllers/UserController.ts (1 hunks)
  • src/backend/user/Entities/User.ts (1 hunks)
  • src/backend/user/Services/UserService.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/backend/user/Services/UserService.ts

[error] 3-24: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/backend/event/Services/EventService.ts

[error] 15-15: Expected an expression but instead found ':'.

Expected an expression here.

(parse)


[error] 3-22: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🔇 Additional comments (2)
src/backend/event/Entities/Event.ts (1)

1-22: Code looks good

The Event class and DBEvent type are well-defined with appropriate default values. This ensures that event objects are consistently initialized.

src/backend/user/Controllers/UserController.ts (1)

46-47: Confirm default sorting order when orderDescending is undefined

When orderDescending is undefined, !orderDescending evaluates to true, resulting in ascending order. Ensure this aligns with the intended default sorting order.

src/backend/event/Controllers/EventController.ts Outdated Show resolved Hide resolved
src/backend/event/Controllers/EventController.ts Outdated Show resolved Hide resolved
src/backend/event/Services/EventService.ts Outdated Show resolved Hide resolved
src/backend/user/Controllers/UserController.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@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

🧹 Nitpick comments (1)
src/backend/event/Services/EventService.ts (1)

5-20: Add JSDoc documentation for the parsing method.

The method would benefit from clear documentation explaining the transformation logic and default values.

Add documentation:

+    /**
+     * Converts a database event record to an Event entity
+     * @param record - The database event record
+     * @returns An Event entity with validated and transformed data
+     * @throws Error if required fields are missing
+     */
     public static parseEvent(record: DBEvent) : Event {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4dc1147 and 756f770.

📒 Files selected for processing (1)
  • src/backend/event/Services/EventService.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
src/backend/event/Services/EventService.ts

[error] 3-22: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

🔇 Additional comments (1)
src/backend/event/Services/EventService.ts (1)

1-1: LGTM! Import follows the architectural pattern.

The import statement correctly follows the Clean Architecture pattern by importing both the domain entity (Event) and its database representation (DBEvent).

src/backend/event/Services/EventService.ts Outdated Show resolved Hide resolved
src/backend/event/Services/EventService.ts Outdated Show resolved Hide resolved
@1989ONCE 1989ONCE self-requested a review December 17, 2024 13:41
daviddaytw
daviddaytw previously approved these changes Dec 17, 2024
src/backend/event/Services/EventService.ts Outdated Show resolved Hide resolved
1989ONCE
1989ONCE previously approved these changes Dec 18, 2024
Extra checkings have been added to service and controller methods
to ensure type safety and null safety, as recommended by coderabbit.

(2024/12/21)
@yeahlowflicker yeahlowflicker dismissed stale reviews from 1989ONCE and daviddaytw via 924f5ef December 20, 2024 16:20
Copy link
Contributor

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

🧹 Nitpick comments (2)
src/backend/event/Services/EventService.ts (1)

3-25: Well-implemented Clean Architecture service layer.

The service effectively fulfills its role in the Clean Architecture by:

  • Converting database types (DBEvent) to domain entities (Event)
  • Handling data validation and transformation
  • Maintaining clear separation of concerns

Consider adding JSDoc comments to document the service's role in the Clean Architecture and any assumptions about the data transformation process.

src/backend/user/Services/UserService.ts (1)

5-12: Enhance method documentation with @throws and @example

The documentation is good but could be more comprehensive by including error scenarios and usage examples.

Consider adding:

     /**
      * Convert a user from default Supabase type to User entity
      * 
      * @param   {DBUser} record   - The record retrieved from Supabase
      * @returns {User}            - Converted user entity
+     * @throws  {Error}           - When uuid field is missing
+     * @example
+     * const dbUser = { uuid: '123', name: 'John', ... }
+     * const user = UserService.parseUser(dbUser)
      * 
      * @author Henry C. (@yeahlowflicker)
      */
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 756f770 and 924f5ef.

📒 Files selected for processing (4)
  • src/backend/event/Controllers/EventController.ts (1 hunks)
  • src/backend/event/Services/EventService.ts (1 hunks)
  • src/backend/user/Controllers/UserController.ts (1 hunks)
  • src/backend/user/Services/UserService.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/backend/user/Controllers/UserController.ts
  • src/backend/event/Controllers/EventController.ts
🔇 Additional comments (3)
src/backend/event/Services/EventService.ts (2)

1-3: LGTM! Good choice of using object literal pattern.

The object literal pattern is a clean and maintainable approach, avoiding the complexity of class-based implementations.


11-19: LGTM! Good handling of types and optional fields.

The implementation shows good practices:

  • Proper type checking for numeric fields
  • Consistent date format using ISO strings
  • Appropriate default values for optional fields
src/backend/user/Services/UserService.ts (1)

1-1: Verify User entity structure matches database schema

Let's ensure the User entity fields align with the database schema and are used consistently across the codebase.

✅ Verification successful

Let me gather more information about the database schema by checking the DBUser type and any SQL schema definitions.


Let me gather one final piece of information about the database types to complete the verification.


Based on the gathered information, I can now provide a final response:

✓ User entity structure aligns with database schema

The User entity fields match the members table schema from the SQL dump:

  • The DBUser type is correctly mapped to the members table Row type
  • User entity fields (id, username, email, phone, avatar, etc.) correspond to the members table columns
  • The parseUser method in UserService properly handles the conversion between database and entity models
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify User entity structure and usage

# Check User entity structure
echo "Checking User entity structure..."
ast-grep --pattern 'class User {
  $$$
}'

# Check for User entity usage patterns
echo "Checking User entity usage..."
rg -A 5 "new User\(\)"

# Check for parseUser method usage
echo "Checking parseUser usage..."
rg -A 5 "parseUser\("

Length of output: 3555


Script:

#!/bin/bash
# Check DBUser type definition
echo "Checking DBUser type definition..."
ast-grep --pattern 'type DBUser = {
  $$$
}'

# Look for SQL schema files
echo "Looking for SQL schema files..."
fd -e sql -x cat {}

# Check for database-related type definitions
echo "Checking database-related types..."
rg -A 10 "interface.*User|type.*User"

Length of output: 29924


Script:

#!/bin/bash
# Check Database type definitions
echo "Checking Database type definitions..."
rg -A 5 "type Database = {" 

# Check members table structure
echo "Checking members table fields..."
rg -A 10 "members.*Row.*="

Length of output: 316

src/backend/event/Services/EventService.ts Show resolved Hide resolved
src/backend/event/Services/EventService.ts Outdated Show resolved Hide resolved
src/backend/user/Services/UserService.ts Outdated Show resolved Hide resolved
src/backend/user/Services/UserService.ts Outdated Show resolved Hide resolved
Copy link
Contributor

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

♻️ Duplicate comments (2)
src/backend/user/Services/UserService.ts (1)

1-3: 🛠️ Refactor suggestion

Consider implementing UserService as a class

The current object literal pattern doesn't fully align with Clean Architecture principles.

src/backend/event/Services/EventService.ts (1)

5-10: ⚠️ Potential issue

Add validation for userID as a required field.

According to the PR objectives, both id and userID are required fields.

 if (!record.id)
     throw new Error('id is a required field')
+if (!record.user_id)
+    throw new Error('user_id is a required field')
🧹 Nitpick comments (4)
src/backend/user/Services/UserService.ts (2)

5-12: Enhance method documentation with error cases

The documentation should include @throws sections to document the error conditions:

  • When record is invalid/null
  • When uuid is missing
     /**
      * Convert a user from default Supabase type to User entity
      * 
      * @param   {DBUser} record   - The record retrieved from Supabase
      * @returns {User}            - Converted user entity
+     * @throws  {Error}           - When record is invalid or null
+     * @throws  {Error}           - When uuid is missing
      * 
      * @author Henry C. (@yeahlowflicker)
      */

1-33: Consider Clean Architecture best practices

To better align with Clean Architecture principles:

  1. Define an interface IUserService to abstract the service layer
  2. Implement dependency injection to improve testability
  3. Consider adding a repository layer to handle data access

Example interface:

interface IUserService {
  parseUser(record: DBUser): User;
  // Add future methods here
}
src/backend/event/Services/EventService.ts (2)

23-23: Consider using current timestamp as default for createdAt.

When created_at is null/undefined, it might be more appropriate to use the current timestamp instead of an empty string, as this field typically represents the record creation time.

-event.createdAt         = record.created_at ? new Date(record.created_at).toISOString() : ""
+event.createdAt         = record.created_at ? new Date(record.created_at).toISOString() : new Date().toISOString()

6-7: Consider integrating with ErrorHandler utility.

Since the PR introduces an ErrorHandler utility for universal error handling, consider using it here instead of throwing raw Error objects. This would ensure consistent error handling across the application.

 if (!record || typeof record !== 'object')
-    throw new Error('Invalid record provided')
+    throw ErrorHandler.createError('INVALID_RECORD', 'Invalid record provided')
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 924f5ef and 7b14a50.

📒 Files selected for processing (2)
  • src/backend/event/Services/EventService.ts (1 hunks)
  • src/backend/user/Services/UserService.ts (1 hunks)
🔇 Additional comments (3)
src/backend/user/Services/UserService.ts (1)

13-29: Add comprehensive validation and sanitization

The current implementation only validates the uuid field.

Let's verify if the User entity provides default values for optional fields:

#!/bin/bash
# Description: Check User entity implementation for default values

# Search for User class definition and constructor
ast-grep --pattern 'class User {
  $$$
  constructor() {
    $$$
  }
  $$$
}'
src/backend/event/Services/EventService.ts (2)

1-3: LGTM! Good implementation of the service pattern.

The object literal pattern is a clean and maintainable approach for implementing services in TypeScript.


25-30: Verify service integration with controllers.

The implementation looks good. Let's verify that this service is properly imported and used in the controllers.

✅ Verification successful

Service integration is properly implemented

The EventService is correctly imported and utilized in the EventController. The parseEvent method is used appropriately in both getEvents and findEventByID methods for converting database events (DBEvent) to the Event type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for EventService imports and usage in controllers
rg -l "import.*EventService" src/
rg "EventService\.parseEvent" src/

Length of output: 327


Script:

#!/bin/bash
# Let's check the actual implementation in EventController to verify correct usage
ast-grep --pattern 'class EventController {
  $$$
}'

Length of output: 8717

Copy link
Member

@daviddaytw daviddaytw left a comment

Choose a reason for hiding this comment

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

LGTM!

@yeahlowflicker yeahlowflicker merged commit e198ba8 into NCUAppTeam:main Dec 21, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request structure refactor, db model design
Development

Successfully merging this pull request may close these issues.

3 participants