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

037 Nov 5 Add Swagger for Section #201

Conversation

HarshP4585
Copy link
Contributor

@HarshP4585 HarshP4585 commented Nov 5, 2024

  • Added Swagger YAML for sections
  • Fixed controller for sections

Affected Issue: #155

Summary by CodeRabbit

  • New Features

    • Introduced new API endpoints for managing sections:
      • GET /sections
      • POST /sections
      • GET /sections/{id}
      • PUT /sections/{id}
      • DELETE /sections/{id}
    • Expanded parameters for creating and updating sections to include assessment_tracker_id, total_questions, and completed_questions.
  • Bug Fixes

    • Improved error handling for missing parameters in section creation and updates.
  • Documentation

    • Added a new schema definition for Section in the API documentation to clarify the required properties.

@HarshP4585 HarshP4585 added the backend Backend related tasks/issues label Nov 5, 2024
@HarshP4585 HarshP4585 self-assigned this Nov 5, 2024
Copy link

coderabbitai bot commented Nov 5, 2024

Walkthrough

The changes in this pull request primarily involve updates to the createSection and updateSectionById functions within the Servers/controllers/section.ctrl.ts file, expanding the required parameters in the request body. These functions now require additional fields: assessment_tracker_id, total_questions, and completed_questions. Corresponding updates were made to the createNewSectionQuery and updateSectionByIdQuery functions in Servers/utils/section.util.ts, which now accommodate the new parameters. Additionally, new API endpoints for managing sections were introduced in Servers/swagger.yaml.

Changes

File Path Change Summary
Servers/controllers/section.ctrl.ts Updated createSection and updateSectionById to require assessment_tracker_id, total_questions, and completed_questions. Enhanced error handling for missing parameters.
Servers/swagger.yaml Added new endpoints: GET /sections, POST /sections, GET /sections/{id}, PUT /sections/{id}, DELETE /sections/{id}. Introduced Section schema definition.
Servers/utils/section.util.ts Modified createNewSectionQuery and updateSectionByIdQuery to include new fields and updated SQL statements accordingly.

Possibly related PRs

Suggested reviewers

  • MuhammadKhalilzadeh

Poem

🐰 In the meadow where data flows,
New sections bloom, as the API grows.
With fields expanded, we dance with glee,
Creating and updating, oh so seamlessly!
A hop, a skip, through code we prance,
Celebrating changes, let’s take a chance! 🌼


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

@MuhammadKhalilzadeh
Copy link
Collaborator

@HarshP4585
conflicts

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

🧹 Outside diff range and nitpick comments (3)
Servers/controllers/section.ctrl.ts (2)

Line range hint 131-169: Remove debug log and extract validation logic.

  1. The debug console.log statement should be removed or enhanced
  2. The validation logic is duplicated from createSection
  3. Error handling could be more consistent

Consider extracting the validation logic into a shared function:

// Add this utility function
function validateSectionData(
  assessment_tracker_id: number,
  total_questions: number,
  completed_questions: number
): { isValid: boolean; error?: string } {
  if (!Number.isInteger(assessment_tracker_id) || assessment_tracker_id <= 0) {
    return { isValid: false, error: "assessment_tracker_id must be a positive integer" };
  }

  if (!Number.isInteger(total_questions) || total_questions <= 0) {
    return { isValid: false, error: "total_questions must be a positive integer" };
  }

  if (!Number.isInteger(completed_questions) || completed_questions < 0) {
    return { isValid: false, error: "completed_questions must be a non-negative integer" };
  }

  if (completed_questions > total_questions) {
    return { isValid: false, error: "completed_questions cannot exceed total_questions" };
  }

  return { isValid: true };
}

Then update the function:

 export async function updateSectionById(req: Request, res: Response): Promise<any> {
-  console.log("updateSectionById");
   try {
     const sectionId = parseInt(req.params.id);
     const {
       assessment_tracker_id,
       name,
       total_questions,
       completed_questions
     } = req.body;

+    const validation = validateSectionData(
+      assessment_tracker_id,
+      total_questions,
+      completed_questions
+    );
+
+    if (!validation.isValid) {
+      return res.status(400).json(STATUS_CODE[400]({ message: validation.error }));
+    }

     // Rest of the function...
   } catch (error) {
+    console.error('Error in updateSectionById:', error);
     return res.status(500).json(STATUS_CODE[500]((error as Error).message));
   }
 }

Line range hint 72-169: Consider implementing request validation middleware.

Both functions would benefit from middleware validation using a schema validation library like Joi or Zod. This would:

  1. Centralize validation logic
  2. Provide consistent error messages
  3. Reduce code duplication
  4. Improve type safety

Would you like me to provide an example implementation using Joi or Zod?

Servers/swagger.yaml (1)

2813-2834: Standardize error response structure.

The 503 error response uses the same schema as 500. Consider differentiating them to provide more specific error information.

 "503":
   description: internal server error
   content:
     application/json:
       type: object
       properties:
         message:
           type: string
-          example: service unavailable
+          example: database connection error
         error:
           type: object
+          properties:
+            code:
+              type: string
+              example: DB_CONNECTION_ERROR
+            details:
+              type: string
+              example: Unable to connect to the database
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8866208 and 9824862.

📒 Files selected for processing (3)
  • Servers/controllers/section.ctrl.ts (3 hunks)
  • Servers/swagger.yaml (2 hunks)
  • Servers/utils/section.util.ts (1 hunks)
🔇 Additional comments (4)
Servers/controllers/section.ctrl.ts (1)

Line range hint 72-169: Verify Swagger documentation alignment.

The changes to the request body parameters in both functions should be reflected in the Swagger documentation.

Let's verify the Swagger documentation:

✅ Verification successful

Swagger documentation is aligned with the implementation

The Swagger documentation correctly reflects all required request body parameters for both POST and PUT endpoints:

  • assessment_tracker_id (integer)
  • name (string)
  • total_questions (integer)
  • completed_questions (integer)

The schema definition and endpoint documentation match the implementation in section.ctrl.ts.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the Swagger documentation matches the new parameters

# Test: Search for section-related endpoints in swagger.yaml
rg -A 10 "/sections:" Servers/swagger.yaml

# Test: Search for section schema definition
rg -A 10 "Section:" Servers/swagger.yaml

Length of output: 533


Script:

#!/bin/bash
# Let's check more context around the Section schema to see all properties
rg -A 15 "Section:" Servers/swagger.yaml

# Let's also check the POST and PUT endpoints documentation
rg -A 20 "post:|put:" Servers/swagger.yaml

Length of output: 12988

Servers/swagger.yaml (1)

Line range hint 2734-3254: LGTM! The sections API implementation is well-structured.

The implementation follows REST best practices, maintains consistency with other endpoints, and provides comprehensive documentation. The suggested improvements will enhance data validation and schema completeness.

Servers/utils/section.util.ts (2)

17-20: Parameters in createNewSectionQuery are correctly defined.

The required fields assessment_tracker_id, name, total_questions, and completed_questions are appropriately included in the function parameters.


24-25: Ensure the order of placeholders matches the values in the INSERT statement.

The placeholders $1, $2, $3, $4 correspond to the fields in the INSERT statement and the values array. Verify that they are correctly ordered to prevent any data mismatch.

To confirm, you can run the following script:

This script searches for the INSERT query pattern and allows you to verify the placeholders and values.

✅ Verification successful

This will help us see the full context of the INSERT statement and the corresponding values array to verify their alignment.


Placeholders and values are correctly ordered in the INSERT statement

The order of placeholders perfectly matches the values array:

  • $1section.assessment_tracker_id
  • $2section.name
  • $3section.total_questions
  • $4section.completed_questions
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the placeholders in the INSERT statement match the values provided.

# Test: Check the order of fields and values in the INSERT query.
ast-grep --pattern $'pool.query(
  "INSERT INTO sections ($_fields) VALUES ($_placeholders) RETURNING *",
  $_values
)'

Length of output: 120


Script:

#!/bin/bash
# Let's try a different approach to find the query pattern
rg -U "INSERT INTO sections.*VALUES.*RETURNING" -A 2

Length of output: 385

Comment on lines +72 to +111
const {
assessment_tracker_id,
name,
total_questions,
completed_questions
} = req.body;

if (
!assessment_tracker_id ||
!name ||
!total_questions ||
!completed_questions
) {
return res
.status(400)
.json(
STATUS_CODE[400]({ message: "name and description are required" })
STATUS_CODE[400]({ message: "assessment_tracker_id, name, total_questions and completed_questions are required" })
);
}

if (MOCK_DATA_ON === "true") {
const newSection = createMockSection({ name, description });
const newSection = createMockSection({
assessment_tracker_id,
name,
total_questions,
completed_questions
});

if (newSection) {
return res.status(201).json(STATUS_CODE[201](newSection));
}

return res.status(503).json(STATUS_CODE[503]({}));
} else {
const newSection = await createNewSectionQuery({ name, description });
const newSection = await createNewSectionQuery({
assessment_tracker_id,
name,
total_questions,
completed_questions
});
Copy link

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 improve error handling.

The function needs additional validation and error handling improvements:

  1. Add type and range validation for numeric fields
  2. Add structured error responses
  3. Add error logging for debugging

Consider applying these improvements:

 export async function createSection(req: Request, res: Response): Promise<any> {
   try {
     const {
       assessment_tracker_id,
       name,
       total_questions,
       completed_questions
     } = req.body;
 
+    // Validate numeric fields
+    if (!Number.isInteger(assessment_tracker_id) || assessment_tracker_id <= 0) {
+      return res.status(400).json(
+        STATUS_CODE[400]({ message: "assessment_tracker_id must be a positive integer" })
+      );
+    }
+
+    if (!Number.isInteger(total_questions) || total_questions <= 0) {
+      return res.status(400).json(
+        STATUS_CODE[400]({ message: "total_questions must be a positive integer" })
+      );
+    }
+
+    if (!Number.isInteger(completed_questions) || completed_questions < 0) {
+      return res.status(400).json(
+        STATUS_CODE[400]({ message: "completed_questions must be a non-negative integer" })
+      );
+    }
+
+    if (completed_questions > total_questions) {
+      return res.status(400).json(
+        STATUS_CODE[400]({ message: "completed_questions cannot exceed total_questions" })
+      );
+    }

     // Rest of the function...
   } catch (error) {
+    console.error('Error in createSection:', error);
     return res.status(500).json(STATUS_CODE[500]((error as Error).message));
   }
 }
📝 Committable suggestion

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

Suggested change
const {
assessment_tracker_id,
name,
total_questions,
completed_questions
} = req.body;
if (
!assessment_tracker_id ||
!name ||
!total_questions ||
!completed_questions
) {
return res
.status(400)
.json(
STATUS_CODE[400]({ message: "name and description are required" })
STATUS_CODE[400]({ message: "assessment_tracker_id, name, total_questions and completed_questions are required" })
);
}
if (MOCK_DATA_ON === "true") {
const newSection = createMockSection({ name, description });
const newSection = createMockSection({
assessment_tracker_id,
name,
total_questions,
completed_questions
});
if (newSection) {
return res.status(201).json(STATUS_CODE[201](newSection));
}
return res.status(503).json(STATUS_CODE[503]({}));
} else {
const newSection = await createNewSectionQuery({ name, description });
const newSection = await createNewSectionQuery({
assessment_tracker_id,
name,
total_questions,
completed_questions
});
const {
assessment_tracker_id,
name,
total_questions,
completed_questions
} = req.body;
// Validate numeric fields
if (!Number.isInteger(assessment_tracker_id) || assessment_tracker_id <= 0) {
return res.status(400).json(
STATUS_CODE[400]({ message: "assessment_tracker_id must be a positive integer" })
);
}
if (!Number.isInteger(total_questions) || total_questions <= 0) {
return res.status(400).json(
STATUS_CODE[400]({ message: "total_questions must be a positive integer" })
);
}
if (!Number.isInteger(completed_questions) || completed_questions < 0) {
return res.status(400).json(
STATUS_CODE[400]({ message: "completed_questions must be a non-negative integer" })
);
}
if (completed_questions > total_questions) {
return res.status(400).json(
STATUS_CODE[400]({ message: "completed_questions cannot exceed total_questions" })
);
}
if (
!assessment_tracker_id ||
!name ||
!total_questions ||
!completed_questions
) {
return res
.status(400)
.json(
STATUS_CODE[400]({ message: "assessment_tracker_id, name, total_questions and completed_questions are required" })
);
}
if (MOCK_DATA_ON === "true") {
const newSection = createMockSection({
assessment_tracker_id,
name,
total_questions,
completed_questions
});
if (newSection) {
return res.status(201).json(STATUS_CODE[201](newSection));
}
return res.status(503).json(STATUS_CODE[503]({}));
} else {
const newSection = await createNewSectionQuery({
assessment_tracker_id,
name,
total_questions,
completed_questions
});

Comment on lines +2787 to +2798
assessment_tracker_id:
type: integer
example: 1
name:
type: string
example: "Network Security"
total_questions:
type: integer
example: 20
completed_questions:
type: integer
example: 20
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add field validations in the request body schema.

Consider adding constraints to ensure data integrity:

 assessment_tracker_id:
   type: integer
   example: 1
+  minimum: 1
 name:
   type: string
   example: "Network Security"
+  minLength: 1
+  maxLength: 255
 total_questions:
   type: integer
   example: 20
+  minimum: 0
 completed_questions:
   type: integer
   example: 20
+  minimum: 0
+  maximum: {total_questions}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +3237 to +3254
Section:
type: object
properties:
id:
type: integer
example: 1
assessment_tracker_id:
type: integer
example: 1
name:
type: string
example: "Network Security"
total_questions:
type: integer
example: 20
completed_questions:
type: integer
example: 20
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance the Section schema definition.

The schema should be consistent with other entities and include additional metadata:

 Section:
   type: object
+  description: Represents a section within an assessment tracker
   properties:
     id:
       type: integer
       example: 1
+      readOnly: true
     assessment_tracker_id:
       type: integer
       example: 1
+      minimum: 1
     name:
       type: string
       example: "Network Security"
+      minLength: 1
+      maxLength: 255
     total_questions:
       type: integer
       example: 20
+      minimum: 0
     completed_questions:
       type: integer
       example: 20
+      minimum: 0
+    created_at:
+      type: string
+      format: date-time
+      example: "2024-10-22T10:15:00Z"
+      readOnly: true
+    updated_at:
+      type: string
+      format: date-time
+      example: "2024-10-23T12:30:00Z"
+      readOnly: true
+  required:
+    - assessment_tracker_id
+    - name
+    - total_questions
+    - completed_questions
📝 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
Section:
type: object
properties:
id:
type: integer
example: 1
assessment_tracker_id:
type: integer
example: 1
name:
type: string
example: "Network Security"
total_questions:
type: integer
example: 20
completed_questions:
type: integer
example: 20
Section:
type: object
description: Represents a section within an assessment tracker
properties:
id:
type: integer
example: 1
readOnly: true
assessment_tracker_id:
type: integer
example: 1
minimum: 1
name:
type: string
example: "Network Security"
minLength: 1
maxLength: 255
total_questions:
type: integer
example: 20
minimum: 0
completed_questions:
type: integer
example: 20
minimum: 0
created_at:
type: string
format: date-time
example: "2024-10-22T10:15:00Z"
readOnly: true
updated_at:
type: string
format: date-time
example: "2024-10-23T12:30:00Z"
readOnly: true
required:
- assessment_tracker_id
- name
- total_questions
- completed_questions

Comment on lines +44 to +47
if(section.assessment_tracker_id) {
fields.push("assessment_tracker_id = $1")
values.push(section.assessment_tracker_id)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Adjust placeholder numbering to handle optional fields gracefully.

Hardcoded placeholders like $1, $2, etc., can cause errors if some optional fields are not provided. Dynamically generating placeholders based on the number of values ensures accurate SQL queries.

Refactored Code:

 let query = "UPDATE sections SET ";
 
 if (section.assessment_tracker_id) {
-  fields.push("assessment_tracker_id = $1");
+  fields.push(`assessment_tracker_id = \$${values.length + 1}`);
   values.push(section.assessment_tracker_id);
 }

 if (section.name) {
-  fields.push("name = $2");
+  fields.push(`name = \$${values.length + 1}`);
   values.push(section.name);
 }

 if (section.total_questions) {
-  fields.push("total_questions = $3");
+  fields.push(`total_questions = \$${values.length + 1}`);
   values.push(section.total_questions);
 }

 if (section.completed_questions) {
-  fields.push("completed_questions = $4");
+  fields.push(`completed_questions = \$${values.length + 1}`);
   values.push(section.completed_questions);
 }

This approach updates the placeholder numbers based on the current length of the values array, maintaining correct alignment between placeholders and values.

Also applies to: 48-51, 52-54, 56-58

Comment on lines +32 to +37
section: {
assessment_tracker_id?: number
name?: string
total_questions?: number
completed_questions?: number
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Review use of optional parameters in updateSectionByIdQuery.

The function accepts optional fields for updating a section. However, hardcoding placeholder numbers may lead to incorrect parameter binding when some fields are omitted.

Suggested Fix: Dynamically assign placeholder numbers.

Modify the code to assign placeholders based on the current length of the values array, ensuring accurate mapping between placeholders and values.

 const fields = [];
 const values = [];
 let query = "UPDATE sections SET ";

 if (section.assessment_tracker_id) {
-  fields.push("assessment_tracker_id = $1");
+  fields.push(`assessment_tracker_id = \$${values.length + 1}`);
   values.push(section.assessment_tracker_id);
 }

 if (section.name) {
-  fields.push("name = $2");
+  fields.push(`name = \$${values.length + 1}`);
   values.push(section.name);
 }

 if (section.total_questions) {
-  fields.push("total_questions = $3");
+  fields.push(`total_questions = \$${values.length + 1}`);
   values.push(section.total_questions);
 }

 if (section.completed_questions) {
-  fields.push("completed_questions = $4");
+  fields.push(`completed_questions = \$${values.length + 1}`);
   values.push(section.completed_questions);
 }

This change ensures that each placeholder $n correctly corresponds to the nth value in the values array.

Committable suggestion skipped: line range outside the PR's diff.

}

if (fields.length === 0) {
throw new Error("No fields to update");
}

query += fields.join(", ") + " WHERE id = $3 RETURNING *";
query += fields.join(", ") + " WHERE id = $5 RETURNING *";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the placeholder for id in the WHERE clause.

The placeholder $5 assumes that there are always four fields before id, which may not be the case with optional fields. Dynamically setting this placeholder ensures the query remains valid.

Suggested Fix:

- query += fields.join(", ") + " WHERE id = $5 RETURNING *";
+ query += fields.join(", ") + ` WHERE id = \$${values.length + 1} RETURNING *`;
 values.push(id);

This change aligns the placeholder for id with the next available position in the values array.

Committable suggestion skipped: line range outside the PR's diff.

@MuhammadKhalilzadeh MuhammadKhalilzadeh merged commit 6c45e6a into bluewave-labs:master Nov 7, 2024
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend Backend related tasks/issues
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants