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

support for bulk update of devices #4047

Merged
merged 2 commits into from
Dec 12, 2024
Merged

support for bulk update of devices #4047

merged 2 commits into from
Dec 12, 2024

Conversation

Baalmart
Copy link
Contributor

@Baalmart Baalmart commented Dec 12, 2024

Description

support for bulk update of devices

Changes Made

  • support for bulk update of devices

Testing

  • Tested locally
  • Tested against staging environment
  • Relevant tests passed: [List test names]

Affected Services

  • Which services were modified:
    • Device Registry

Endpoints Ready for Testing

  • New endpoints ready for testing:
    • BULK update devices: PUT: {{baseUrl}}/api/v2/devices/bulk

API Documentation Updated?

  • Yes, API documentation was updated
  • No, API documentation does not need updating

Summary by CodeRabbit

  • New Features

    • Introduced bulk update functionality for devices, allowing multiple devices to be updated in a single request.
    • Added a new route for bulk updates at the /bulk endpoint.
    • Implemented a validation function for bulk update requests.
  • Bug Fixes

    • Enhanced error handling and logging for bulk update operations.

Copy link
Contributor

coderabbitai bot commented Dec 12, 2024

📝 Walkthrough
📝 Walkthrough

Walkthrough

The changes in this pull request introduce a new method for bulk updating devices to the device controller and the device model, along with a new route and validation for handling these updates. The updateManyDevicesOnPlatform method is added to manage bulk updates, while the bulkModify method in the device model facilitates the actual database updates. A new route is created for handling PUT requests to /bulk, and a validation function ensures that the incoming requests meet the necessary criteria. These modifications enhance the device management system's capabilities.

Changes

File Path Change Summary
src/device-registry/controllers/create-device.js Added method updateManyDevicesOnPlatform(req, res, next) for bulk updates of devices.
src/device-registry/models/Device.js Added method async bulkModify({ filter, update, opts }, next) for bulk updates in the model; updated existing modify method to include logging.
src/device-registry/routes/v2/devices.js Added route router.put("/bulk", ...) for bulk device updates; included validation middleware.
src/device-registry/utils/create-device.js Added method updateManyDevicesOnPlatform(request, next) for processing bulk updates; modified delete method to indicate it's disabled.
src/device-registry/validators/device.validators.js Added validation function validateBulkUpdateDevices for validating bulk update requests.

Possibly related issues

Possibly related PRs

Suggested reviewers

  • Codebmk
  • BenjaminSsempala
  • OchiengPaul442

🎉 In the realm of devices, we now can say,
Bulk updates are here, brightening the way!
With validation in place and routes all set,
Managing devices is easier, you bet!
So here's to the changes, a round of applause,
For smoother updates, we cheer and we pause! 🎊


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. (Experiment)
  • @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

codecov bot commented Dec 12, 2024

Codecov Report

Attention: Patch coverage is 2.56410% with 76 lines in your changes missing coverage. Please review.

Project coverage is 11.68%. Comparing base (21041fa) to head (9608d2d).
Report is 3 commits behind head on staging.

Files with missing lines Patch % Lines
...rc/device-registry/validators/device.validators.js 4.00% 24 Missing ⚠️
src/device-registry/controllers/create-device.js 0.00% 21 Missing ⚠️
src/device-registry/utils/create-device.js 0.00% 19 Missing ⚠️
src/device-registry/models/Device.js 0.00% 12 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           staging    #4047      +/-   ##
===========================================
- Coverage    11.73%   11.68%   -0.05%     
===========================================
  Files          114      114              
  Lines        15349    15427      +78     
  Branches       319      319              
===========================================
+ Hits          1801     1803       +2     
- Misses       13548    13624      +76     
Files with missing lines Coverage Δ
src/device-registry/routes/v2/devices.js 100.00% <100.00%> (ø)
src/device-registry/models/Device.js 13.36% <0.00%> (-0.85%) ⬇️
src/device-registry/utils/create-device.js 5.38% <0.00%> (-0.33%) ⬇️
src/device-registry/controllers/create-device.js 3.20% <0.00%> (-0.18%) ⬇️
...rc/device-registry/validators/device.validators.js 24.41% <4.00%> (-8.37%) ⬇️

Copy link
Contributor

Device registry changes in this PR available for preview here

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

🧹 Outside diff range and nitpick comments (5)
src/device-registry/utils/create-device.js (2)

729-729: Remove unused variable 'providedDeviceIds'

The variable providedDeviceIds is declared but not used in the function. Removing it will clean up the code and prevent any confusion.

Apply this diff:

- const providedDeviceIds = new Set(deviceIds.map((id) => id.toString()));

756-756: Ensure correct merging of filters

Using Object.assign(filter, additionalFilter); could potentially overwrite existing properties in the filter object. To avoid unintended side effects, consider merging the filters carefully, possibly using the spread operator or combining them with $and.

Example:

- Object.assign(filter, additionalFilter);
+ const combinedFilter = { $and: [filter, additionalFilter] };
src/device-registry/models/Device.js (1)

671-671: Remove unnecessary new option in updateMany

The new option is not applicable to updateMany operations in Mongoose; it is used with findOneAndUpdate. Removing it will clarify the code and prevent confusion.

Apply this diff:

       {
-        new: true,
         runValidators: true,
         ...opts,
       }
src/device-registry/controllers/create-device.js (2)

800-800: Consider enhancing debug logging.

To improve debugging capabilities, consider logging the request payload size and validation details.

 logText("the BULK update operation starts....");
+logObject("bulk update payload", { size: req.body.length });

826-830: Consider enriching the success response.

The success response could be more informative by including summary statistics of the bulk update operation.

 return res.status(status).json({
   message: result.message,
   success: true,
-  bulk_update_notes: result.data,
+  bulk_update_notes: result.data,
+  summary: {
+    total: result.data.length,
+    successful: result.data.filter(note => note.success).length,
+    failed: result.data.filter(note => !note.success).length
+  }
 });
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 21041fa and 9b472b6.

📒 Files selected for processing (5)
  • src/device-registry/controllers/create-device.js (1 hunks)
  • src/device-registry/models/Device.js (1 hunks)
  • src/device-registry/routes/v2/devices.js (2 hunks)
  • src/device-registry/utils/create-device.js (1 hunks)
  • src/device-registry/validators/device.validators.js (2 hunks)
🔇 Additional comments (4)
src/device-registry/utils/create-device.js (1)

788-788: ⚠️ Potential issue

Fix string interpolation in error logging statement

The error logging statement on line 788 uses single quotes, which do not support template literals in JavaScript. To correctly interpolate the error message, please use backticks.

Apply this diff to fix the issue:

- logger.error('🐛🐛 Bulk Update Error: ${error.message}');
+ logger.error(`🐛🐛 Bulk Update Error: ${error.message}`);

Likely invalid or redundant comment.

src/device-registry/routes/v2/devices.js (1)

129-135: Bulk update route added appropriately

The new route for bulk updating devices is correctly defined with the necessary validations and controller method. This enhances the API's capabilities effectively.

src/device-registry/validators/device.validators.js (1)

773-799: Ensure allowed fields in updateData are consistent

Double-check that the allowedFields array includes all valid fields and is consistent with the fields validated in validateUpdateDevice. This ensures that only permissible fields are updated.

src/device-registry/controllers/create-device.js (1)

798-852: LGTM! The implementation follows consistent patterns.

The new updateManyDevicesOnPlatform method follows the established controller patterns for error handling, request processing, and response formatting. The implementation is clean and maintainable.

Comment on lines 780 to 782
...responseFromBulkModifyDevices,
metadata: {
totalDevicesUpdated: responseFromBulkModifyDevices.modifiedCount,
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

Correct property access for modifiedCount in response metadata

The modifiedCount property is nested under data in the response from bulkModify. Access it using responseFromBulkModifyDevices.data.modifiedCount to ensure accurate metadata.

Apply this diff:

- totalDevicesUpdated: responseFromBulkModifyDevices.modifiedCount,
+ totalDevicesUpdated: responseFromBulkModifyDevices.data.modifiedCount,
📝 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
...responseFromBulkModifyDevices,
metadata: {
totalDevicesUpdated: responseFromBulkModifyDevices.modifiedCount,
...responseFromBulkModifyDevices,
metadata: {
totalDevicesUpdated: responseFromBulkModifyDevices.data.modifiedCount,

message: "No devices were updated",
data: {
modifiedCount: 0,
matchedCount: bulkUpdateResult.matchedCount,
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

Correct matchedCount property in response data

The bulkUpdateResult from updateMany does not have a matchedCount property. Use bulkUpdateResult.n to get the number of matched documents.

Apply this diff:

- matchedCount: bulkUpdateResult.matchedCount,
+ matchedCount: bulkUpdateResult.n,
📝 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
matchedCount: bulkUpdateResult.matchedCount,
matchedCount: bulkUpdateResult.n,

.exists()
.withMessage("updateData must be provided in the request body")
.bail()
.isObject()
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

Replace .isObject() with a custom validator

The .isObject() method is not a standard validator in express-validator. Using a custom validator ensures that updateData is correctly validated as an object.

Apply this diff:

-     .isObject()
-     .withMessage("updateData must be an object")
+     .custom((value) => {
+       if (typeof value !== 'object' || Array.isArray(value) || value === null) {
+         throw new Error('updateData must be an object');
+       }
+       return true;
+     })
📝 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
.isObject()
.custom((value) => {
if (typeof value !== 'object' || Array.isArray(value) || value === null) {
throw new Error('updateData must be an object');
}
return true;
})

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

🧹 Outside diff range and nitpick comments (9)
src/device-registry/models/Device.js (1)

682-683: Update response structure for MongoDB compatibility

The nModified property is deprecated in newer MongoDB versions. Consider using modifiedCount instead.

 data: {
-  modifiedCount: bulkUpdateResult.nModified,
-  matchedCount: bulkUpdateResult.n,
+  modifiedCount: bulkUpdateResult.modifiedCount,
+  matchedCount: bulkUpdateResult.matchedCount,
 },
src/device-registry/validators/device.validators.js (2)

739-740: Extract MAX_BULK_UPDATE_DEVICES to constants

Move the magic number to a configuration constant for better maintainability.

+const MAX_BULK_UPDATE_DEVICES = 30;
-      const MAX_BULK_UPDATE_DEVICES = 30;
       if (value.length > MAX_BULK_UPDATE_DEVICES) {

777-803: Extract allowed fields to a reusable constant

The allowed fields list should be extracted to a reusable constant to maintain DRY principle and ensure consistency across the codebase.

+const ALLOWED_DEVICE_UPDATE_FIELDS = [
+  "visibility",
+  "long_name",
+  // ... other fields
+];

-      const allowedFields = [
-        "visibility",
-        "long_name",
-        // ... other fields
-      ];
+      const allowedFields = ALLOWED_DEVICE_UPDATE_FIELDS;
src/device-registry/controllers/create-device.js (2)

800-801: Enhance logging for bulk operations

Add structured logging with operation metadata for better monitoring and debugging.

-      logText("the BULK update operation starts....");
+      logger.info("Bulk update operation started", {
+        deviceCount: req.body.deviceIds.length,
+        tenant: req.query.tenant,
+        updateFields: Object.keys(req.body.updateData)
+      });

815-819: Add request tracking for bulk operations

Consider adding request tracking for bulk operations to help with monitoring and debugging.

Consider implementing:

  1. Request correlation IDs
  2. Operation timing metrics
  3. Progress tracking for large bulk updates
src/device-registry/utils/create-device.js (4)

749-757: Consider adding index hint for bulk operations.

While the filter construction is correct, for large-scale operations, consider adding an index hint to optimize the query performance.

 const filter = {
   _id: { $in: deviceIds },
+  $hint: { _id: 1 }  // Add index hint for better performance
 };

766-776: Consider implementing batch processing for large updates.

For better scalability, consider implementing batch processing when dealing with a large number of devices. This would help manage memory usage and prevent potential timeouts.

 // Perform bulk update
+const BATCH_SIZE = 1000;
+const batches = [];
+for (let i = 0; i < deviceIds.length; i += BATCH_SIZE) {
+  const batchIds = deviceIds.slice(i, i + BATCH_SIZE);
+  const batchFilter = { ...filter, _id: { $in: batchIds } };
+  batches.push(
+    DeviceModel(tenant).bulkModify(
+      {
+        filter: batchFilter,
+        update: updateData,
+        opts,
+      },
+      next
+    )
+  );
+}
+const results = await Promise.all(batches);

779-786: Enhance response metadata with timing information.

Consider adding execution timing information to the metadata for monitoring and optimization purposes.

 return {
   ...responseFromBulkModifyDevices,
   metadata: {
     totalDevicesUpdated: responseFromBulkModifyDevices.data.modifiedCount,
     requestedDeviceIds: deviceIds,
     existingDeviceIds: Array.from(existingDeviceIds),
+    executionTimeMs: Date.now() - startTime,  // Add execution time
   },
 };

787-796: Enhance error logging for bulk operations.

The error handling is good, but consider adding more context about the failed operation in the logs.

-logger.error(`🐛🐛 Bulk Update Error: ${error.message}`);
+logger.error(`🐛🐛 Bulk Update Error for ${deviceIds.length} devices: ${error.message}`, {
+  tenant,
+  deviceCount: deviceIds.length,
+  errorStack: error.stack
+});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9b472b6 and 9608d2d.

📒 Files selected for processing (4)
  • src/device-registry/controllers/create-device.js (1 hunks)
  • src/device-registry/models/Device.js (1 hunks)
  • src/device-registry/utils/create-device.js (1 hunks)
  • src/device-registry/validators/device.validators.js (2 hunks)
🔇 Additional comments (2)
src/device-registry/utils/create-device.js (2)

716-747: Robust device existence validation implementation.

The implementation thoroughly validates device existence with detailed error reporting. The use of Sets for comparison is an efficient approach.


759-764: Verify the impact of runValidators on bulk performance.

The runValidators: true option ensures data integrity but might impact performance for large updates. Consider adding a warning in the documentation about potential performance implications for bulk operations.

✅ Verification successful

The previous pattern didn't work well with ast-grep, so I'm using ripgrep to search for Schema definitions and any existing documentation about validation performance in bulk operations. This will help us understand the validation rules in place and any documented performance considerations.


I'm searching for actual bulk update operations and any performance-related documentation in the codebase, particularly around the file in question. This will help us understand if there are any existing patterns or documented performance considerations for bulk operations with validation.


The runValidators option is safe to use in this bulk update context.

Based on the codebase analysis:

  1. The update operation is used in a controlled context with a specific filter and limited scope
  2. The schema validations are relatively simple, mostly consisting of:
    • Basic type checks (String, Number, Boolean)
    • Required field validations
    • Trim operations
    • Simple enum validations

The validation overhead is minimal for these types of validations, and the data integrity benefits outweigh any minor performance impact. The codebase shows consistent use of validation across models, indicating it's a deliberate architectural choice for maintaining data quality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential validation rules that might impact performance
ast-grep --pattern 'const $schema = new Schema({
  $$$
})'

Length of output: 60


Script:

#!/bin/bash
# Let's try a different approach to find mongoose schema definitions and validation rules
rg -A 10 "new Schema\(" 

# Also search for any documentation about bulk operations and validation
rg -i "runValidators.*bulk|bulk.*runValidators"

# Search for any performance-related comments or documentation
rg -i "performance.*validation|validation.*performance"

Length of output: 67382


Script:

#!/bin/bash
# Let's search for any bulk operations in the codebase
rg -B 2 -A 2 "updateMany|update\(\{.*\}\s*,\s*\{.*\}\s*,\s*\{.*runValidators.*\}\)"

# Also search for any mongoose update operations with options
rg -B 2 -A 2 "update.*\{.*\}.*\{.*\}.*\{.*\}"

# Look for any performance-related comments or documentation in the file
rg -C 5 "performance|optimization" src/device-registry/utils/create-device.js

Length of output: 46454

Comment on lines +677 to +697
if (bulkUpdateResult.nModified > 0) {
return {
success: true,
message: `Successfully modified ${bulkUpdateResult.nModified} devices`,
data: {
modifiedCount: bulkUpdateResult.nModified,
matchedCount: bulkUpdateResult.n,
},
status: httpStatus.OK,
};
} else {
return {
success: false,
message: "No devices were updated",
data: {
modifiedCount: 0,
matchedCount: bulkUpdateResult.n,
},
status: httpStatus.NOT_FOUND,
};
}
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

Consider using transactions for atomic updates

The bulk update operation should be wrapped in a transaction to ensure atomicity across multiple document updates. This prevents partial updates in case of failures.

 if (bulkUpdateResult.nModified > 0) {
+  const session = await mongoose.startSession();
+  try {
+    await session.withTransaction(async () => {
       return {
         success: true,
         message: `Successfully modified ${bulkUpdateResult.nModified} devices`,
         data: {
           modifiedCount: bulkUpdateResult.nModified,
           matchedCount: bulkUpdateResult.n,
         },
         status: httpStatus.OK,
       };
+    });
+  } finally {
+    session.endSession();
+  }
 }

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

Copy link
Contributor

Device registry changes in this PR available for preview here

@Baalmart Baalmart merged commit c8cac9f into staging Dec 12, 2024
50 of 52 checks passed
@Baalmart Baalmart deleted the bulk-update-devices branch December 12, 2024 09:44
@Baalmart Baalmart mentioned this pull request Dec 12, 2024
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant