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

Implement Dummy API for Project Understanding #445

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Conversation

devleejb
Copy link
Member

@devleejb devleejb commented Feb 1, 2025

What this PR does / why we need it:

  • Implement a Dummy API for Project Understanding
  • Feel free to share your feedback! This is a very basic and initial approach.

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:


Additional documentation:


Checklist:

  • Added relevant tests or not required
  • Didn't break anything

Summary by CodeRabbit

  • New Features

    • Introduced a new POST /hello endpoint that accepts a user's nickname and returns a personalized greeting message.
  • Refactor

    • Standardized API response formatting and enhanced error feedback, ensuring clearer input validation and consistent messaging.
    • Restructured existing endpoints for improved readability and consistency in formatting.

@devleejb devleejb self-assigned this Feb 1, 2025
Copy link
Contributor

coderabbitai bot commented Feb 1, 2025

Walkthrough

This pull request introduces a new POST /hello endpoint in the API specification with the operation ID helloCodePair. It adds new schemas (HelloRequest and HelloResponse) and a corresponding route for handling requests. The update also includes Go model enhancements such as error functions, validation methods, and a switch from a root GET endpoint to a layered architecture (repository, service, handler) for the hello functionality. Additionally, new REST helper functions for error conversion and request handling have been added, along with formatting improvements in existing endpoints.

Changes

File(s) Change Summary
api/backend-openapi-spec.json Added new POST /hello endpoint with operation ID helloCodePair; introduced HelloRequest and HelloResponse schemas; adjusted formatting for existing endpoints.
backend-go/api/codepair/v1/models/... Added new error functions (RequiredFieldError, MinLengthError), new structs HelloRequest and HelloResponse, updated HttpExceptionResponse.StatusCode type, and added a Validate method for HelloRequest.
backend-go/internal/core/hello/... Introduced the hello package with new components: Handler (with method HelloCodePair), CodePairVisitor struct, Repository interface, and Service (with method HelloCodePair).
backend-go/internal/infra/database/mongo/hello.go Added a new Mongo repository implementation, HelloRepository, with a ReadHelloMessageFor method to generate greeting messages.
backend-go/internal/server/routes.go Removed the old root GET route and added the new POST /hello route, setting up the repository, service, and handler for the hello endpoint.
backend-go/internal/transport/rest/... Added helper functions and error handling: ConvertErrorToResponse, several HTTP error response generators, a new InternalServerError variable, and functions for binding and validating requests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler as HelloHandler
    participant Service as HelloService
    participant Repo as HelloRepository
    
    Client->>Handler: POST /hello (with JSON {nickname})
    Handler->>Handler: Bind & Validate request (using HelloRequest.Validate)
    Handler->>Service: Invoke HelloCodePair(visitor)
    Service->>Repo: ReadHelloMessageFor(visitor)
    Repo-->>Service: "Hello, <nickname>"
    Service-->>Handler: return greeting message
    Handler-->>Client: 200 OK with HelloResponse (message)
Loading

Possibly related PRs

Suggested labels

enhancement 🌟

Suggested reviewers

  • blurfx
  • krapie
  • binary-ho
  • sihyeong671
  • window9u
  • kokodak

Poem

Hop, hop, I scurry through code so neat,
New endpoints and models, a fresh digital treat.
With validations and errors handled just right,
My little rabbit heart dances with delight.
In fields of REST and routes so clear,
I hop to celebrate the new changes here! 🐇


📜 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 b1bcdc7 and 3f38f34.

📒 Files selected for processing (1)
  • backend-go/internal/transport/rest/error_responses.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend-go/internal/transport/rest/error_responses.go

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

🧹 Nitpick comments (8)
backend-go/internal/transport/rest/converter.go (1)

8-9: Enhance function documentation.

The current comment is too brief. Consider adding more details about the function's behavior, parameters, and return value.

-// ConvertErrorToResponse converts an error to a response.
+// ConvertErrorToResponse converts an error to an HTTP exception response.
+// It maps specific error types to appropriate HTTP responses:
+// - InternalServerError -> 500 Internal Server Error
+// For unhandled error types, it returns an invalid JSON error response.
+//
+// Parameters:
+//   - err: The error to convert
+//
+// Returns:
+//   - models.HttpExceptionResponse: The corresponding HTTP exception response
backend-go/internal/transport/rest/error_responses.go (3)

8-13: Add input validation to helper function.

The helper function should validate the status code and message to ensure they are appropriate.

 func newHttpExceptionResponse(statusCode int32, message string) models.HttpExceptionResponse {
+	if statusCode < 100 || statusCode > 599 {
+		statusCode = http.StatusInternalServerError
+	}
+	if message == "" {
+		message = http.StatusText(int(statusCode))
+	}
 	return models.HttpExceptionResponse{
 		StatusCode: statusCode,
 		Message:    message,
 	}
 }

20-23: Enhance validation error message.

Consider including more context in the validation error message to help clients better understand and fix the issue.

 func NewValidationErrorResponse(reason string) models.HttpExceptionResponse {
-	return newHttpExceptionResponse(http.StatusBadRequest, reason)
+	return newHttpExceptionResponse(http.StatusBadRequest, fmt.Sprintf("Validation failed: %s", reason))
 }

25-28: Enhance internal server error message.

Consider providing more context in the internal server error message while keeping sensitive details private.

 func NewInternalServerErrorResponse() models.HttpExceptionResponse {
-	return newHttpExceptionResponse(http.StatusInternalServerError, "Internal Server Error")
+	return newHttpExceptionResponse(http.StatusInternalServerError, 
+		"An unexpected error occurred. Please try again later or contact support if the problem persists.")
 }
backend-go/internal/server/routes.go (2)

10-12: Consider flexible repository injection.
While the direct instantiation here is fine, you might later prefer dependency injection or mocking to facilitate testing and environment-based configuration.


19-19: Consider versioning or grouping this route.
Using a version prefix (e.g., /api/v1/hello) or grouping related routes can help maintain backward compatibility and code organization as the project grows.

backend-go/api/codepair/v1/models/model_hello_request.go (1)

3-7: Provide optional length checks for Nickname.
While the field is basic, consider applying upper-limits in validation to avoid potential issues with overly large inputs.

backend-go/internal/infra/database/mongo/hello.go (1)

5-10: Consider making the greeting format configurable.

The greeting message is currently hardcoded. Consider making it configurable through repository initialization.

Example implementation:

 type HelloRepository struct{
+	greetingFormat string
 }

 func NewHelloRepository() HelloRepository {
-	return HelloRepository{}
+	return HelloRepository{
+		greetingFormat: "Hello, %s!",
+	}
 }

 func (h HelloRepository) ReadHelloMessageFor(codePairVisitor hello.CodePairVisitor) (string, error) {
-	return "Hello, " + codePairVisitor.Nickname + "!", nil
+	return fmt.Sprintf(h.greetingFormat, codePairVisitor.Nickname), nil
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 04b398e and 5fdfdbc.

📒 Files selected for processing (16)
  • api/backend-openapi-spec.json (76 hunks)
  • backend-go/api/codepair/v1/models/errors.go (1 hunks)
  • backend-go/api/codepair/v1/models/model_hello_request.go (1 hunks)
  • backend-go/api/codepair/v1/models/model_hello_response.go (1 hunks)
  • backend-go/api/codepair/v1/models/model_http_exception_response.go (1 hunks)
  • backend-go/api/codepair/v1/models/validators.go (1 hunks)
  • backend-go/internal/core/hello/handler.go (1 hunks)
  • backend-go/internal/core/hello/model.go (1 hunks)
  • backend-go/internal/core/hello/repository.go (1 hunks)
  • backend-go/internal/core/hello/service.go (1 hunks)
  • backend-go/internal/infra/database/mongo/hello.go (1 hunks)
  • backend-go/internal/server/routes.go (1 hunks)
  • backend-go/internal/transport/rest/converter.go (1 hunks)
  • backend-go/internal/transport/rest/error_responses.go (1 hunks)
  • backend-go/internal/transport/rest/errors.go (1 hunks)
  • backend-go/internal/transport/rest/handler_utils.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • backend-go/internal/core/hello/model.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
backend-go/api/codepair/v1/models/errors.go

[warning] 9-9: redefines-builtin-id: redefinition of the built-in function min

(revive)


10-10: stringintconv: conversion from int to string yields a string of one rune, not a string of digits

(govet)

backend-go/internal/core/hello/handler.go

25-25: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.BindAndValidateRequest(e github.com/labstack/echo/v4.Context, req github.com/yorkie-team/codepair/backend/internal/transport/rest.request) error

(wrapcheck)


33-33: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.NewErrorResponse(e github.com/labstack/echo/v4.Context, err error) error

(wrapcheck)


36-36: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.NewOkResponse(e github.com/labstack/echo/v4.Context, resp interface{}) error

(wrapcheck)

🔇 Additional comments (9)
backend-go/internal/transport/rest/errors.go (1)

1-8: LGTM!

The error variable is well-documented and follows Go's error message style guide.

backend-go/api/codepair/v1/models/model_http_exception_response.go (1)

6-6: LGTM!

The change from float32 to int32 is appropriate as HTTP status codes are integers.

backend-go/internal/server/routes.go (3)

5-6: No issues with introducing these imports.
They seamlessly integrate the new repository and service into the layered architecture.


13-15: Service instantiation aligns with standard layering.
The creation of helloService from helloRepository is straightforward and clear.


16-18: Handler setup follows best practices.
Creating a dedicated handler (helloHandler) for this service is properly structured.

backend-go/api/codepair/v1/models/model_hello_response.go (1)

3-7: Response struct is succinct.
Defining a single field Message is straightforward. No further changes appear necessary.

backend-go/internal/core/hello/repository.go (1)

3-6: LGTM! Well-designed interface.

The interface follows Go best practices with:

  • Clear, single responsibility
  • Descriptive method name and parameters
  • Good documentation
  • Proper error handling
backend-go/api/codepair/v1/models/validators.go (1)

3-13: LGTM! Well-structured validation.

The validation logic follows good practices:

  • Checks for required field first
  • Clear minimum length validation
  • Uses descriptive error messages
backend-go/internal/core/hello/handler.go (1)

21-39: LGTM! The handler implementation follows best practices.

The implementation correctly:

  • Binds and validates the request
  • Handles errors appropriately
  • Returns proper HTTP responses
🧰 Tools
🪛 golangci-lint (1.62.2)

25-25: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.BindAndValidateRequest(e github.com/labstack/echo/v4.Context, req github.com/yorkie-team/codepair/backend/internal/transport/rest.request) error

(wrapcheck)


33-33: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.NewErrorResponse(e github.com/labstack/echo/v4.Context, err error) error

(wrapcheck)


36-36: error returned from external package is unwrapped: sig: func github.com/yorkie-team/codepair/backend/internal/transport/rest.NewOkResponse(e github.com/labstack/echo/v4.Context, resp interface{}) error

(wrapcheck)

Comment on lines +10 to +15
switch {
case errors.Is(err, InternalServerError):
return NewInternalServerErrorResponse()
default:
return NewInvalidJsonErrorResponse()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in the default case.

The default case returns an invalid JSON error response, which might be misleading for other types of errors. Consider adding more error cases and using a generic error response for unhandled errors.

 func ConvertErrorToResponse(err error) models.HttpExceptionResponse {
 	switch {
 	case errors.Is(err, InternalServerError):
 		return NewInternalServerErrorResponse()
+	case errors.Is(err, &models.ValidationError{}):
+		return NewValidationErrorResponse(err.Error())
 	default:
-		return NewInvalidJsonErrorResponse()
+		return newHttpExceptionResponse(http.StatusInternalServerError, err.Error())
 	}
 }
📝 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
switch {
case errors.Is(err, InternalServerError):
return NewInternalServerErrorResponse()
default:
return NewInvalidJsonErrorResponse()
}
func ConvertErrorToResponse(err error) models.HttpExceptionResponse {
switch {
case errors.Is(err, InternalServerError):
return NewInternalServerErrorResponse()
case errors.Is(err, &models.ValidationError{}):
return NewValidationErrorResponse(err.Error())
default:
return newHttpExceptionResponse(http.StatusInternalServerError, err.Error())
}
}

Comment on lines 9 to 11
func MinLengthError(field string, min int) error {
return errors.New(field + " must be at least " + string(min) + " characters")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix string conversion in error message.

The current implementation uses string(min) which converts the integer to a single rune character instead of a string representation of the number.

Use strconv.Itoa to properly convert the integer to a string:

+import "strconv"

 func MinLengthError(field string, min int) error {
-	return errors.New(field + " must be at least " + string(min) + " characters")
+	return errors.New(field + " must be at least " + strconv.Itoa(min) + " characters")
 }
📝 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
func MinLengthError(field string, min int) error {
return errors.New(field + " must be at least " + string(min) + " characters")
}
import "strconv"
func MinLengthError(field string, min int) error {
return errors.New(field + " must be at least " + strconv.Itoa(min) + " characters")
}
🧰 Tools
🪛 golangci-lint (1.62.2)

[warning] 9-9: redefines-builtin-id: redefinition of the built-in function min

(revive)


10-10: stringintconv: conversion from int to string yields a string of one rune, not a string of digits

(govet)

Comment on lines +12 to +14
func (h HelloRepository) ReadHelloMessageFor(codePairVisitor hello.CodePairVisitor) (string, error) {
return "Hello, " + codePairVisitor.Nickname + "!", nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add input validation and error handling.

The current implementation could be improved with:

  1. Input validation to prevent nil pointer dereference
  2. Error handling for edge cases

Consider this safer implementation:

 func (h HelloRepository) ReadHelloMessageFor(codePairVisitor hello.CodePairVisitor) (string, error) {
+	if codePairVisitor.Nickname == "" {
+		return "", errors.New("invalid visitor: nickname is required")
+	}
 	return "Hello, " + codePairVisitor.Nickname + "!", nil
 }
📝 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
func (h HelloRepository) ReadHelloMessageFor(codePairVisitor hello.CodePairVisitor) (string, error) {
return "Hello, " + codePairVisitor.Nickname + "!", nil
}
func (h HelloRepository) ReadHelloMessageFor(codePairVisitor hello.CodePairVisitor) (string, error) {
if codePairVisitor.Nickname == "" {
return "", errors.New("invalid visitor: nickname is required")
}
return "Hello, " + codePairVisitor.Nickname + "!", nil
}

func (s Service) HelloCodePair(e echo.Context, codePairVisitor CodePairVisitor) (string, error) {
helloMessage, err := s.helloRepository.ReadHelloMessageFor(codePairVisitor)
if err != nil {
e.Logger().Fatal(err)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Replace Fatal logging with Error level logging.

Using Fatal logging here is problematic as it terminates the program. For recoverable errors, use Error level logging instead to maintain service availability.

Apply this diff:

-		e.Logger().Fatal(err)
+		e.Logger().Error(err)
📝 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
e.Logger().Fatal(err)
e.Logger().Error(err)

helloMessage, err := s.helloRepository.ReadHelloMessageFor(codePairVisitor)
if err != nil {
e.Logger().Fatal(err)
return "", rest.InternalServerError
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Wrap the error to preserve context.

The original error context is lost when returning a generic internal server error. Wrap the error to maintain the error chain for better debugging.

Apply this diff:

-		return "", rest.InternalServerError
+		return "", fmt.Errorf("failed to read hello message: %w", err)

Don't forget to add the import:

+import "fmt"
📝 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 "", rest.InternalServerError
package hello
import (
// other existing imports
"fmt"
// possibly other imports
)
func someFunction(...) (string, error) {
// ... previous code
// Updated error return to preserve context
return "", fmt.Errorf("failed to read hello message: %w", err)
}

Comment on lines +28 to +29
resp := ConvertErrorToResponse(err)
return e.JSON(int(resp.StatusCode), ConvertErrorToResponse(err))
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate error conversion.

ConvertErrorToResponse is called twice unnecessarily. Store the result in a variable to avoid the duplicate conversion.

Apply this diff:

-	resp := ConvertErrorToResponse(err)
-	return e.JSON(int(resp.StatusCode), ConvertErrorToResponse(err))
+	resp := ConvertErrorToResponse(err)
+	return e.JSON(int(resp.StatusCode), resp)
📝 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
resp := ConvertErrorToResponse(err)
return e.JSON(int(resp.StatusCode), ConvertErrorToResponse(err))
resp := ConvertErrorToResponse(err)
return e.JSON(int(resp.StatusCode), resp)

Comment on lines +21 to +31
"default": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HelloResponse"
}
}
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Define specific response status codes instead of using default.

Using "default" response makes the API behavior less predictable. Define specific success and error status codes.

Apply this diff:

-        "default": {
+        "200": {
           "description": "",
           "content": {
             "application/json": {
               "schema": {
                 "$ref": "#/components/schemas/HelloResponse"
               }
             }
           }
+        },
+        "400": {
+          "description": "Invalid request parameters",
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/HttpExceptionResponse"
+              }
+            }
+          }
+        },
+        "500": {
+          "description": "Internal server error",
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/HttpExceptionResponse"
+              }
+            }
+          }
         }
📝 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
"default": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HelloResponse"
}
}
}
}
},
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HelloResponse"
}
}
}
},
"400": {
"description": "Invalid request parameters",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HttpExceptionResponse"
}
}
}
},
"500": {
"description": "Internal server error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HttpExceptionResponse"
}
}
}
}
},

backend-go/api/codepair/v1/models/errors.go Outdated Show resolved Hide resolved
Comment on lines +1 to +7
package models

type HelloRequest struct {

// New nickname to say hello
Nickname string `json:"nickname"`
}
Copy link
Member

Choose a reason for hiding this comment

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

How about writing a Request, Response, and Validator of the same model (Hello) in the same file? It seems unnecessary to write the Request and Response in different files.

Maybe it would be better to write them together in the router file.

Copy link
Member

Choose a reason for hiding this comment

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

I personally agree with @blurfx's suggestion, as this style of micro-separating so-called DTOs is not a common approach in Golang and may lead to an unnecessary proliferation of files for DTO management.

Copy link
Member Author

@devleejb devleejb Feb 2, 2025

Choose a reason for hiding this comment

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

One important thing to keep in mind is that all request and response types are auto-generated from the OpenAPI specification.

Validator

Since these files are auto-generated, declaring validation functions within the same file is risky, as they might be overwritten during regeneration.

Type Placement

Moving these types to their corresponding packages is quite challenging, as it is auto-generated. Additionally, the directory structure is similar to yorkie. I believe these types correspond to files like admin.pb.go and yorkie.pb.go.

DTO

I’m unsure how we should manage request and response types for REST APIs in Go. Could you provide an example or some guidance on this?

Copy link
Member

Choose a reason for hiding this comment

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

I didn’t realize these files were auto-generated by OpenAPI generation. If that’s the case, I think it’s great!

Copy link
Member

@krapie krapie left a comment

Choose a reason for hiding this comment

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

Thank you for promptly creating the sample API.

The code style closely resembles Java, which feels like a breath of fresh air for us Golang users.

I've also left some comments regarding its style! 😄

Comment on lines +1 to +7
package models

type HelloRequest struct {

// New nickname to say hello
Nickname string `json:"nickname"`
}
Copy link
Member

Choose a reason for hiding this comment

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

I personally agree with @blurfx's suggestion, as this style of micro-separating so-called DTOs is not a common approach in Golang and may lead to an unnecessary proliferation of files for DTO management.

backend-go/internal/core/hello/handler.go Outdated Show resolved Hide resolved
backend-go/internal/core/hello/service.go Show resolved Hide resolved
return fmt.Errorf("error: %w", err)
})
// Repositories
helloRepository := mongo.NewHelloRepository()
Copy link
Member

Choose a reason for hiding this comment

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

Initializing the database layer here does not seem familiar to me, as it exposes the layer at a two-layer depth. How about moving the database layer initialization to the service layer?

Copy link
Member Author

Choose a reason for hiding this comment

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

I’m not sure if initializing the database layer within the service layer is a good idea. I believe it could reduce testability. I’d prefer to maintain the DI (Dependency Injection) structure.

What do you think? @window9u

Copy link
Contributor

Choose a reason for hiding this comment

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

I also agree with krapie’s comment. In my view, the responsibility of RegisterRoutes is to receive handlers and middlewares as arguments and configure the routes.

And I expected initialize and DI repository, services, handlers in New() in internal/server/server.go

Copy link
Member

Choose a reason for hiding this comment

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

Initializing the repository at the router level isn't a good idea to me either.

Considering DI and testability, I think it would be better to initialize at the root level (cmd/codepair/main.go) and pass it to the services below.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think it's a good idea to initialize not only the repository, but also the service and handler at the router level.

I think it's better to inject the Handler at the router level, so that we can configure the E2E test in the way we want later.

So, I'd prefer to initialize Repository, Service, Handler, etc. at a layer one level higher than the router level.

@@ -0,0 +1,16 @@
package rest
Copy link
Member

Choose a reason for hiding this comment

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

How about changing the package name to http? This would better represent the files within the package and their role in handling HTTP request/response logic.

Copy link
Member Author

Choose a reason for hiding this comment

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

I’d love to, but naming it http would be redundant with net/http. We could use an alias like http2 instead…

Copy link
Member

@krapie krapie Feb 2, 2025

Choose a reason for hiding this comment

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

Maybe we can use package name aliasing for net/http, renaming it to something like nethttp, as shown below:

import nethttp "net/http"

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 (2)
backend-go/internal/core/hello/handler.go (2)

21-26: Add context timeout and enhance input validation.

Consider the following improvements:

  1. Add context timeout to prevent long-running requests
  2. Add specific validation for the nickname field (e.g., length, allowed characters)

Example implementation:

 func (h Handler) HelloCodePair(e echo.Context) error {
+    ctx, cancel := context.WithTimeout(e.Request().Context(), 5*time.Second)
+    defer cancel()
+
     data := new(models.HelloRequest)
 
     if err := rest.BindAndValidateRequest(e, data); err != nil {
         return err
     }
+
+    if err := validateNickname(data.Nickname); err != nil {
+        return rest.NewBadRequestError(e, err)
+    }

31-33: Enhance error handling with specific error types.

Consider returning more specific error responses based on the error type from the service layer.

Example implementation:

     if err != nil {
-        return rest.NewErrorResponse(e, err)
+        switch {
+        case errors.Is(err, ErrInvalidVisitor):
+            return rest.NewBadRequestError(e, err)
+        case errors.Is(err, ErrServiceUnavailable):
+            return rest.NewServiceUnavailableError(e, err)
+        default:
+            return rest.NewInternalServerError(e, err)
+        }
     }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5fdfdbc and b1bcdc7.

📒 Files selected for processing (5)
  • backend-go/api/codepair/v1/models/errors.go (1 hunks)
  • backend-go/api/codepair/v1/models/model_http_exception_response.go (1 hunks)
  • backend-go/internal/core/hello/handler.go (1 hunks)
  • backend-go/internal/core/hello/service.go (1 hunks)
  • package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend-go/api/codepair/v1/models/model_http_exception_response.go
  • backend-go/api/codepair/v1/models/errors.go
  • backend-go/internal/core/hello/service.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
backend-go/internal/core/hello/handler.go

6-6: could not import github.com/yorkie-team/codepair/backend/internal/transport/rest (-: # github.com/yorkie-team/codepair/backend/internal/transport/rest
internal/transport/rest/error_responses.go:10:15: cannot use statusCode (variable of type int32) as int value in struct literal)

(typecheck)

🔇 Additional comments (2)
package.json (1)

18-18: Enhance Backend Generation Command with Explicit Type Mapping

Adding the --type-mappings int32=int option now ensures that any integer field defined as int32 in your OpenAPI specification is generated as a Go int in the backend code. This change is aligned with the PR objectives for implementing the new Dummy API endpoint and related models. Please verify that this mapping is consistent with your project's data type expectations and that it won’t lead to potential type misinterpretations in the generated code.

backend-go/internal/core/hello/handler.go (1)

13-18: LGTM! Well-structured constructor following dependency injection pattern.

The constructor is well-documented and correctly implements dependency injection.

Comment on lines +9 to +11
type Handler struct {
helloService Service
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Service is implemented as a concrete type rather than an interface.

  • The search for a type Service interface definition returned no results.
  • The HelloCodePair method is defined on a concrete Service type in backend-go/internal/core/hello/service.go.
  • If the design aims to use a Service interface for dependency injection and flexibility, this struct should be refactored to an interface.
🔗 Analysis chain

Verify Service interface definition.

The Handler struct depends on a Service type that needs to be properly defined with the required methods.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the Service interface definition

# Look for Service interface definition
ast-grep --pattern 'type Service interface {
  $$$
}'

# Look for Service implementation
ast-grep --pattern 'type $_ struct {
  $$$
}

func ($_ $_) HelloCodePair($_, $_) ($_, $_) {
  $$$
}'

Length of output: 158


Script:

#!/bin/bash
# Check for the Service interface definition using ripgrep
echo "Searching for 'type Service interface' definitions:"
rg -n "type Service interface" .

# Additionally, search for any mention of HelloCodePair 
echo "Searching for 'HelloCodePair' method definitions:"
rg -n "func .*HelloCodePair" .

Length of output: 548

Comment on lines +3 to +7
import (
"github.com/labstack/echo/v4"
"github.com/yorkie-team/codepair/backend/api/codepair/v1/models"
"github.com/yorkie-team/codepair/backend/internal/transport/rest"
)
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Type Mismatch Issue in newHttpExceptionResponse
The function in backend-go/internal/transport/rest/error_responses.go is declared with statusCode as an int32 parameter, but it directly assigns this value to StatusCode in a models.HttpExceptionResponse, which is defined as an int. This mismatch requires either an explicit conversion (e.g., StatusCode: int(statusCode)) or a change of the parameter’s type to int to ensure type compatibility.

  • Location to change:
    • backend-go/internal/transport/rest/error_responses.go: newHttpExceptionResponse
🔗 Analysis chain

Verify rest package compatibility.

The static analysis indicates a type mismatch in the rest package where statusCode (int32) is being used where an int is expected. This could affect error handling in this handler.


I'll gather more details about the definition of HttpExceptionResponse in the models package to verify if there is any type mismatch with the statusCode parameter in the rest package.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the rest package implementation for type compatibility issues

# Check the error_responses.go file implementation
ast-grep --pattern 'type statusCode int32'

# Check for any type conversion between int32 and int in error handling
rg -A 5 'statusCode.*int'

Length of output: 583


Script:

#!/bin/bash
# Check the models package for the HttpExceptionResponse type definition and its fields.
rg -C 5 "type HttpExceptionResponse" --type go

Length of output: 724

🧰 Tools
🪛 golangci-lint (1.62.2)

6-6: could not import github.com/yorkie-team/codepair/backend/internal/transport/rest (-: # github.com/yorkie-team/codepair/backend/internal/transport/rest
internal/transport/rest/error_responses.go:10:15: cannot use statusCode (variable of type int32) as int value in struct literal)

(typecheck)

@devleejb devleejb requested review from blurfx and krapie February 2, 2025 11:02
return fmt.Errorf("error: %w", err)
})
// Repositories
helloRepository := mongo.NewHelloRepository()
Copy link
Member

Choose a reason for hiding this comment

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

I don't think it's a good idea to initialize not only the repository, but also the service and handler at the router level.

I think it's better to inject the Handler at the router level, so that we can configure the E2E test in the way we want later.

So, I'd prefer to initialize Repository, Service, Handler, etc. at a layer one level higher than the router level.

)

type Handler struct {
helloService Service
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
helloService Service
helloService *Service

How about having fields with pointer types instead of value types?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

5 participants