Repository: airqo-platform/AirQo-api
Pull request #4513: [Device Registry] Implement CQRS pattern with dual database connections
Author: login: Baalmart
Labels: name: ready for review
Created: 2025-03-02T08:55:26Z | Merged: 2025-03-03T16:28:30Z

## Description

This pull request adds support for the Command Query Responsibility Segregation (CQRS) pattern to our microservice by enabling connections to separate command and query databases. The implementation maintains backward compatibility with our existing multi-tenant architecture while allowing explicit separation between read and write operations.

## Changes Made

 

- [ ] Added configuration for separate command (write) and query (read) database connections
- [ ]  Modified database connection logic to establish and maintain dual connections
- [ ]  Created dedicated tenant functions for command and query operations
- [ ]  Implemented backward compatibility for existing code using operation type parameter
- [ ]  Enhanced error handling and logging for both database connections
- [ ]  Updated database naming convention to support CQRS separation

## Testing

- [ ] Tested locally
- [ ] Tested against staging environment
- [ ] Relevant tests passed: [List test names]

## Affected Services

- [ ] Which services were modified:
  - [x] Device Registry
     
## API Documentation Updated?

- [ ] Yes, API documentation was updated
- [x] No, API documentation does not need updating

## Additional Notes

### Constants Configuration
Add the following to your constants file:
```
// For separate database servers
COMMAND_MONGO_URI: 'mongodb://command-db-server:27017',
QUERY_MONGO_URI: 'mongodb://query-db-server:27017',

// If using the same server initially, you can omit these
// and the code will fall back to using MONGO_URI for both
```
2. Usage in Services/Controllers
Write Operations (Command):
```
const { getCommandModelByTenant } = require('@config/database');

// In your service or controller
async function createRecord(data, tenantId) {
  const Model = getCommandModelByTenant(tenantId, 'CollectionName', schema);
  return await Model.create(data);
}
```
Read Operations (Query):
```
const { getQueryModelByTenant } = require('@config/database');

// In your service or controller
async function getRecords(filter, tenantId) {
  const Model = getQueryModelByTenant(tenantId, 'CollectionName', schema);
  return await Model.find(filter);
}
```
Using Backward Compatible Methods:
```
const { getModelByTenant } = require('@config/database');

// For write operations
const writeModel = getModelByTenant(tenantId, 'CollectionName', schema, 'command');

// For read operations (default)
const readModel = getModelByTenant(tenantId, 'CollectionName', schema);
```
3. Migration Strategy

- Update the database configuration file with the new code
- Initially configure both COMMAND_MONGO_URI and QUERY_MONGO_URI to point to the same database server
- Start using the new explicit functions in new code development
- Gradually update existing code to use the appropriate model functions
- Once ready to scale, modify URIs to point to separate database servers

4. Testing Considerations

- Add unit tests for both command and query database operations
- Test the backward compatibility functions
- Verify connection error handling for both databases
- Test multi-tenant isolation in both command and query databases

5. Performance Monitoring

- Add metrics for read vs. write operations
- Monitor connection pools to ensure they're properly sized
- Track query execution times for both databases
- Set up alerts for connection failures to either database

This implementation provides a foundation for CQRS while maintaining compatibility with existing code, allowing for a gradual migration to the new pattern.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enhanced database connectivity with separate handling for write and read operations, improving performance, stability, and multi-tenant support.

- **Chores**
  - Expanded configuration options across production, staging, and development environments with new settings for command and query MongoDB URIs, ensuring more flexible and robust connection management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/src/device-registry/.env.example b/src/device-registry/.env.example
index 701d71b820b..e4cfd2443a1 100644
--- a/src/device-registry/.env.example
+++ b/src/device-registry/.env.example
@@ -9,6 +9,12 @@ MONGO_GCE_URI=
 MONGO_URI_PROD=
 MONGO_URI_STAGE=
 MONGO_URI_DEV=
+COMMAND_MONGO_URI_PROD=
+COMMAND_MONGO_URI_STAGE=
+COMMAND_MONGO_URI_DEV=
+QUERY_MONGO_URI_PROD=
+QUERY_MONGO_URI_STAGE=
+QUERY_MONGO_URI_DEV=
 MONGO_DEV=
 MONGO_TEST=
 MONGO_PROD=
diff --git a/src/device-registry/config/database.js b/src/device-registry/config/database.js
index 48eb41155b2..3692c2eacb1 100644
--- a/src/device-registry/config/database.js
+++ b/src/device-registry/config/database.js
@@ -4,12 +4,15 @@ mongoose.set("useFindAndModify", false);
 mongoose.set("useCreateIndex", true);
 mongoose.set("debug", false);
 const constants = require("./constants");
-const URI = constants.MONGO_URI;
 const log4js = require("log4js");
 const logger = log4js.getLogger(
   `${constants.ENVIRONMENT} -- dbConnection-config`
 );
 
+// Database URIs for CQRS pattern
+const COMMAND_URI = constants.COMMAND_MONGO_URI || constants.MONGO_URI || "";
+const QUERY_URI = constants.QUERY_MONGO_URI || constants.MONGO_URI || "";
+
 const options = {
   useCreateIndex: true,
   useNewUrlParser: true,
@@ -22,45 +25,149 @@ const options = {
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${
+        err && err.message ? err.message : "Unknown reason"
+      }`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+
+/**
+ * Get a tenant-specific command database (for write operations)
+ */
+function getCommandTenantDB(tenantId, modelName, schema) {
+  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
+  if (commandMongoDB) {
+    const db = commandMongoDB.useDb(dbName, { useCache: true });
+    db.model(modelName, schema);
+    return db;
+  }
+  throw new Error("Command database connection not established");
+}
 
-function getTenantDB(tenantId, modelName, schema) {
+/**
+ * Get a tenant-specific query database (for read operations)
+ */
+function getQueryTenantDB(tenantId, modelName, schema) {
+  // const dbName = `${constants.DB_NAME}_query_${tenantId}`;
   const dbName = `${constants.DB_NAME}_${tenantId}`;
-  if (mongodb) {
-    const db = mongodb.useDb(dbName, { useCache: true });
+  if (queryMongoDB) {
+    const db = queryMongoDB.useDb(dbName, { useCache: true });
     db.model(modelName, schema);
     return db;
   }
+  throw new Error("Query database connection not established");
+}
+
+/**
+ * Get a command model (for write operations) for a specific tenant
+ */
+function getCommandModelByTenant(tenantId, modelName, schema) {
+  const tenantDb = getCommandTenantDB(tenantId, modelName, schema);
+  return tenantDb.model(modelName);
 }
 
-function getModelByTenant(tenantId, modelName, schema) {
-  const tenantDb = getTenantDB(tenantId, modelName, schema);
+/**
+ * Get a query model (for read operations) for a specific tenant
+ */
+function getQueryModelByTenant(tenantId, modelName, schema) {
+  const tenantDb = getQueryTenantDB(tenantId, modelName, schema);
   return tenantDb.model(modelName);
 }
 
-module.exports = { getModelByTenant, getTenantDB, connectToMongoDB };
+/**
+ * Backward compatible function to get tenant database
+ * Now accepts an optional operationType parameter
+ */
+function getTenantDB(tenantId, modelName, schema, operationType = "query") {
+  if (operationType === "command") {
+    return getCommandTenantDB(tenantId, modelName, schema);
+  }
+  return getQueryTenantDB(tenantId, modelName, schema);
+}
+
+/**
+ * Backward compatible function to get tenant model
+ * Now accepts an optional operationType parameter
+ */
+function getModelByTenant(
+  tenantId,
+  modelName,
+  schema,
+  operationType = "query"
+) {
+  if (operationType === "command") {
+    return getCommandModelByTenant(tenantId, modelName, schema);
+  }
+  return getQueryModelByTenant(tenantId, modelName, schema);
+}
+
+module.exports = {
+  getModelByTenant,
+  getTenantDB,
+  connectToMongoDB,
+  getCommandModelByTenant,
+  getQueryModelByTenant,
+  getCommandTenantDB,
+  getQueryTenantDB,
+};
diff --git a/src/device-registry/config/environments/development.js b/src/device-registry/config/environments/development.js
index 986dbc8a733..ab4c3504422 100644
--- a/src/device-registry/config/environments/development.js
+++ b/src/device-registry/config/environments/development.js
@@ -8,6 +8,8 @@ const logger = log4js.getLogger(`${this.ENVIRONMENT} -- constants-config`);
 const devConfig = {
   DEFAULT_COHORT: process.env.DEV_DEFAULT_COHORT,
   MONGO_URI: process.env.MONGO_URI_DEV,
+  COMMAND_MONGO_URI: process.env.COMMAND_MONGO_URI_DEV,
+  QUERY_MONGO_URI: process.env.QUERY_MONGO_URI_DEV,
   DB_NAME: process.env.MONGO_DEV,
   REDIS_SERVER: process.env.REDIS_SERVER_DEV,
   REDIS_PORT: process.env.REDIS_PORT,
diff --git a/src/device-registry/config/environments/production.js b/src/device-registry/config/environments/production.js
index 9aaee3ef34b..66c7eb94093 100644
--- a/src/device-registry/config/environments/production.js
+++ b/src/device-registry/config/environments/production.js
@@ -8,6 +8,8 @@ const logger = log4js.getLogger(`${this.ENVIRONMENT} -- constants-config`);
 const prodConfig = {
   DEFAULT_COHORT: process.env.PROD_DEFAULT_COHORT,
   MONGO_URI: process.env.MONGO_URI_PROD,
+  COMMAND_MONGO_URI: process.env.COMMAND_MONGO_URI_PROD,
+  QUERY_MONGO_URI: process.env.QUERY_MONGO_URI_PROD,
   DB_NAME: process.env.MONGO_PROD,
   REDIS_SERVER: process.env.REDIS_SERVER,
   REDIS_PORT: process.env.REDIS_PORT,
diff --git a/src/device-registry/config/environments/staging.js b/src/device-registry/config/environments/staging.js
index 0047d230684..7848d662099 100644
--- a/src/device-registry/config/environments/staging.js
+++ b/src/device-registry/config/environments/staging.js
@@ -8,6 +8,8 @@ const logger = log4js.getLogger(`${this.ENVIRONMENT} -- constants-config`);
 const stageConfig = {
   DEFAULT_COHORT: process.env.STAGE_DEFAULT_COHORT,
   MONGO_URI: process.env.MONGO_URI_STAGE,
+  COMMAND_MONGO_URI: process.env.COMMAND_MONGO_URI_STAGE,
+  QUERY_MONGO_URI: process.env.QUERY_MONGO_URI_STAGE,
   DB_NAME: process.env.MONGO_STAGE,
   REDIS_SERVER: process.env.REDIS_SERVER,
   REDIS_PORT: process.env.REDIS_PORT,


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🧹 Nitpick comments (15)</summary><blockquote>

<details>
<summary>src/auth-service/utils/common/generate-filter.js (1)</summary><blockquote>

`99-123`: **Consider validating the ObjectId before assigning to the filter.**  
While the addition of `tenantSettings` correctly merges parameters from `req.query` and `req.params`, passing potentially invalid IDs directly to `ObjectId()` may lead to unhandled exceptions if the ID is malformed. Adding a brief check or try/catch for `id` would help guarantee safer validations.

</blockquote></details>
<details>
<summary>src/auth-service/routes/v2/tenant-settings.routes.js (1)</summary><blockquote>

`51-57`: **Reusing the same controller method for GET /:id may cause confusion.**  
Although it’s possible to handle all GET requests in `list`, separating single-item retrieval logic into its own controller method often improves maintainability and clarity.

</blockquote></details>
<details>
<summary>src/auth-service/models/TenantSettings.js (3)</summary><blockquote>

`21-65`: **The register static method is comprehensive but might blend concerns.**  
While the method correctly distinguishes between successful and unsuccessful creations, consider separating the duplicate-check from the creation logic for cleaner error handling and potential reusability.

---

`105-143`: **modify method is straightforward but lacks concurrency safeguards.**  
Concurrent updates may overwrite data. If concurrency is expected, consider using a version key or optimistic locking to ensure consistent record updates.

---

`144-185`: **remove method handles edge cases well but could return more details.**  
The method returns minimal information about the removed document. Including additional metadata (timestamps, user context, etc.) can be beneficial for audit logs or security.

</blockquote></details>
<details>
<summary>src/auth-service/models/User.js (1)</summary><blockquote>

`477-530`: **Remove large blocks of commented-out legacy code**  
Retaining commented-out code bloats the file and can cause confusion. Consider removing it altogether since version control can track old approaches.  


```diff
-        // Old constants-based code block ...
-        // ...
```

</blockquote></details>
<details>
<summary>src/auth-service/controllers/tenant-settings.controller.js (1)</summary><blockquote>

`1-231`: **Consider refactoring repetitive request-handling logic**  
Each method (create/list/update/delete) mostly repeats the same pattern of error extraction, tenant fallback, and response formatting. You could abstract these into helper functions for clearer, succinct code while maintaining consistent responses.

</blockquote></details>
<details>
<summary>src/auth-service/validators/tenant-settings.validators.js (4)</summary><blockquote>

`7-42`: **Consolidate tenant validation logic**  
Using `oneOf()` for a single array of validations is valid but somewhat redundant. Since `query("tenant").optional()` ensures the field is either absent or among the specified values, you could simplify this logic while still restricting tenant values. This is a style preference, but a single `query("tenant")` chain might suffice.

---

`44-46`: **Modularize repeated validations for listing endpoints**  
Both `list` and `listInformation` share the same `validateTenant` logic. This is acceptable, but if new listing endpoints appear, consider grouping them into a single object for better scalability.

---

`48-105`: **Avoid repetitive patterns in create validations**  
Validations for `defaultNetwork`, `defaultNetworkRole`, `defaultGroup`, and `defaultGroupRole` share mostly identical rules. You could introduce a helper function (e.g., `validateObjectIdField(fieldName, optional = false)`) to remove duplication and centralize ObjectId logic.  

```diff
+function validateObjectIdField(fieldName, isOptional = false) {
+  const chain = body(fieldName);
+  return isOptional ? chain.optional() : chain.exists().withMessage(`${fieldName} is missing`);
+  // chain trim, isMongoId, sanitizer...
+}
```

---

`107-146`: **Mirror DRY principle in update validations**  
Similar to the 'create' validations, `update` uses repeating patterns for each field. A single helper function for validating optional or required ObjectId fields could reduce verbosity and maintenance costs.

</blockquote></details>
<details>
<summary>src/device-registry/config/database.js (2)</summary><blockquote>

`47-62`: **Event handlers are well-structured**  
Glancing at `setupConnectionHandlers`, everything is straightforward. Re-enable the commented-out `logger.info` lines if you ever need detailed debugging logs.

---

`90-117`: **Tenant-based separation**  
Defining `getCommandTenantDB` and `getQueryTenantDB` for different DBs by tenant is aligned with multi-tenant best practices. Confirm each tenant’s DB actually exists in the cluster or is created dynamically.

</blockquote></details>
<details>
<summary>src/auth-service/config/database.js (2)</summary><blockquote>

`8-8`: **Utilize logger for environment checks**  
Your logger initialization is straightforward. Consider adding a log statement to confirm if `COMMAND_URI` or `QUERY_URI` is missing to preempt potential connection errors.

---

`50-62`: **Connection handlers**  
Similar to the other config, your event handlers are minimal but effective. Consider logging successful connections in production if you need to diagnose issues quickly.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7179ab2d97211b34b6222472375bb02f616ba71b and 7543642a5786d6d4b73a4637cde39dcef312f636.

</details>

<details>
<summary>📒 Files selected for processing (10)</summary>

* `src/auth-service/config/database.js` (3 hunks)
* `src/auth-service/controllers/tenant-settings.controller.js` (1 hunks)
* `src/auth-service/models/TenantSettings.js` (1 hunks)
* `src/auth-service/models/User.js` (2 hunks)
* `src/auth-service/routes/v2/index.js` (1 hunks)
* `src/auth-service/routes/v2/tenant-settings.routes.js` (1 hunks)
* `src/auth-service/utils/common/generate-filter.js` (1 hunks)
* `src/auth-service/utils/tenant-settings.util.js` (1 hunks)
* `src/auth-service/validators/tenant-settings.validators.js` (1 hunks)
* `src/device-registry/config/database.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Actions: code-coverage-workflow</summary>

<details>
<summary>src/auth-service/config/database.js</summary>

[error] 3-3: MongooseError: The `uri` parameter to `openUri()` must be a string, got 'undefined'. Make sure the first parameter to `mongoose.connect()` or `mongoose.createConnection()` is a string.

</details>
<details>
<summary>src/device-registry/config/database.js</summary>

[error] 4-4: MongooseError: The `uri` parameter to `openUri()` must be a string, got 'undefined'. Make sure the first parameter to `mongoose.connect()` or `mongoose.createConnection()` is a string.

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (4)</summary>

* GitHub Check: build-push-deploy-device-registry
* GitHub Check: build-push-deploy-auth-service
* GitHub Check: Analyze (python)
* GitHub Check: Analyze (javascript)

</details>

<details>
<summary>🔇 Additional comments (23)</summary><blockquote>

<details>
<summary>src/auth-service/routes/v2/index.js (1)</summary>

`30-30`: **Looks good adding the tenant-settings route.**  
This addition is straightforward and consistent with existing route definitions, ensuring that the new endpoints can be accessed under `/tenant-settings`.

</details>
<details>
<summary>src/auth-service/routes/v2/tenant-settings.routes.js (5)</summary>

`1-16`: **CORS headers and middleware look properly configured.**  
The headers function ensures broad accessibility, and the usage of `tenantSettingsValidations.pagination` is well-placed to handle pagination for subsequent routes.

---

`19-25`: **GET / route is well-structured.**  
The route’s validation chain (list → setJWTAuth → authJWT → controller.list) seems orderly. It ensures authorized and validated requests, aligning with best practices for secure endpoints.

---

`27-33`: **POST / route handles creation flow properly.**  
Using the create validation before authorization helps fail fast on invalid input, enhancing readability and maintainability.

---

`35-41`: **PUT /:id route updates tenant settings effectively.**  
The route addresses updates with a well-organized chain of validations and authentication steps, mirroring the patterns in the codebase.

---

`43-49`: **DELETE /:id route is consistent with the existing CRUD design.**  
The route’s usage of `tenantSettingsController.delete` is properly sequenced after relevant validations and authentication.

</details>
<details>
<summary>src/auth-service/models/TenantSettings.js (2)</summary>

`13-19`: **Schema fields appear suitable for referencing a multi-tenant model.**  
Each reference (e.g., defaultNetwork, defaultGroup) is strongly typed to Mongoose ObjectIds, promoting data consistency. Ensure that referenced collections (Network, Role, Group) exist in each tenant’s database to prevent runtime resolution issues.

---

`188-205`: **Mongoose model retrieval logic is pragmatic and consistent with multi-tenant usage.**  
Utilizing `getModelByTenant` ensures each tenant is managed by a unique Mongoose connection. This approach effectively avoids naming collisions.

</details>
<details>
<summary>src/auth-service/models/User.js (4)</summary>

`20-20`: **Use of the new TenantSettingsModel import looks appropriate**  
This addition cleanly aligns with the new tenant-specific configuration approach.

---

`438-452`: **Ensure robust error-handling for tenant settings retrieval**  
Fetching and verifying tenant settings is handled here. The logic appears correct, but confirm that DB errors and network issues are adequately caught under broader error handling.

---

`457-461`: **Dynamic defaults for network roles**  
Referencing `tenantSettings.defaultNetwork` ensures flexibility across tenants. This is a welcome improvement over hard-coded constants.

---

`469-473`: **Dynamic defaults for group roles**  
Likewise, using `tenantSettings.defaultGroup` keeps group roles tenant-specific. This is consistent with the CQRS multi-tenant vision.

</details>
<details>
<summary>src/auth-service/utils/tenant-settings.util.js (1)</summary>

`1-112`: **Verify safety of calling `.toLowerCase()` on tenant**  
Although the code handles a default tenant, if `tenant` is ever missing or not a string, `.toLowerCase()` may throw. Consider validating or coercing `tenant` to a string before usage.

</details>
<details>
<summary>src/auth-service/validators/tenant-settings.validators.js (3)</summary>

`1-6`: **Use consistent naming in imports & constants**  
The overall imports and constant references look consistent. However, consider verifying that the `@config/constants` file has up-to-date tenant definitions and that all environment-based constants align with the new validators.

---

`148-148`: **Explicitly handle “deleteTenantSetting” validations**  
Combining `validateTenant` and `validateIdParam` is effective. Ensure that you also handle any additional rules for deletions (e.g., preventing deletion of default system settings) as needed in the controller or service layer.

---

`150-157`: **Export is consistent and clear**  
Exporting both single and composite validators in an organized manner is helpful. This is a well-defined approach for scaling future validations.

</details>
<details>
<summary>src/device-registry/config/database.js (4)</summary>

`30-46`: **Separate connections for CQRS**  
Creating distinct connections for command/read aligns well with CQRS. Keep an eye on additional resource usage and confirm that `poolSize` remains optimal for each connection.

---

`66-86`: **Graceful error handling on connection setup**  
You correctly wrap connection logic in a `try/catch`. Verifying environment variables ensures stability. Also confirm that your global error handlers do not swallow other system exceptions.

---

`119-160`: **Consistent naming for command/query model retrieval**  
`getCommandModelByTenant` and `getQueryModelByTenant` nicely separate read and write concerns. Make sure to document these methods in your dev guide so future maintainers can quickly pick up the usage pattern.

---

`162-170`: **Well-organized module exports**  
Exporting all relevant getters in one object is beneficial for discoverability. Good job keeping backward compatibility for `getTenantDB` and `getModelByTenant`.

</details>
<details>
<summary>src/auth-service/config/database.js (3)</summary>

`33-49`: **Establish separate command/query connections**  
This approach parallels the device-registry config. Verify the `dbName` is correct in your environment variables so the system differentiates read/write DBs properly.

---

`66-87`: **Wrap strategies in robust error handling**  
The try/catch helps ensure that the application logs the root cause of a failed connection. If `commandDB` or `queryDB` is `null`, throw a descriptive error to facilitate debugging.

---

`119-160`: **Clear function naming and backward compatibility**  
`getCommandModelByTenant`/`getQueryModelByTenant` plus optional `operationType` is a clean approach to preserve old code usage. Good job ensuring minimal refactoring costs.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-02T08:58:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>src/device-registry/config/environments/staging.js (1)</summary><blockquote>

`8-43`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Verify environment variable setup in deployment pipelines**

While the configuration looks correct, it's important to ensure that these new environment variables (COMMAND_MONGO_URI_STAGE and QUERY_MONGO_URI_STAGE) are properly set up in your deployment pipelines for the staging environment.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if the new environment variables have values in current environment
# Note: This will only work if the environment has these variables set

echo "Checking if new environment variables are set:"
if [ -z "$COMMAND_MONGO_URI_STAGE" ]; then
  echo "Warning: COMMAND_MONGO_URI_STAGE is not set"
else
  echo "COMMAND_MONGO_URI_STAGE is set"
fi

if [ -z "$QUERY_MONGO_URI_STAGE" ]; then
  echo "Warning: QUERY_MONGO_URI_STAGE is not set"
else
  echo "QUERY_MONGO_URI_STAGE is set"
fi

# Look for references to these new environment variables in deployment scripts
echo "Looking for references to new environment variables in CI/CD scripts:"
fd -e yml -e yaml -e json | xargs grep -l "COMMAND_MONGO_URI_STAGE\|QUERY_MONGO_URI_STAGE" || echo "No references found in CI/CD files"
```

Length of output: 645

---



</details>

**Action Required: Update Staging Pipeline to Include New Environment Variables**  
Our review verification indicates that the new environment variables—`COMMAND_MONGO_URI_STAGE` and `QUERY_MONGO_URI_STAGE`—are currently not set in the deployment environment and aren’t referenced in any CI/CD configuration files. Although the code in `src/device-registry/config/environments/staging.js` appears syntactically correct, the missing environment values could lead to runtime issues in staging.

- **Ensure** these variables are provisioned in your staging deployment pipeline.
- **Update** any relevant CI/CD configuration files to reference these keys.
- **Verify** that dependent services and scripts can handle or fallback when these values are provided.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 31-31: This property value named BIGQUERY_DEVICE_UPTIME_TABLE is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named BIGQUERY_DEVICE_UPTIME_TABLE


(lint/suspicious/noDuplicateObjectKeys)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>src/device-registry/config/environments/development.js (1)</summary><blockquote>

`8-42`: **Consider documentation for local development setup**

With the introduction of these new database connection URIs, developers setting up local environments will need guidance on how to properly configure these new variables. Consider adding a note to the relevant developer documentation explaining the CQRS pattern and how to set up these connections locally.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 30-30: This property value named BIGQUERY_DEVICE_UPTIME_TABLE is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named BIGQUERY_DEVICE_UPTIME_TABLE


(lint/suspicious/noDuplicateObjectKeys)

</details>

</details>

</blockquote></details>
<details>
<summary>src/device-registry/.env.example (1)</summary><blockquote>

`1-126`: **Consider adding documentation on migration strategy**

The PR objectives mention a migration strategy that involves "initially configuring both command and query URIs to point to the same database server." It would be helpful to include a comment in the .env.example file that explains this strategy to guide developers during the transition period.


You could add a comment above these new variables explaining the migration approach, for example:

```diff
9 MONGO_URI_PROD=
10 MONGO_URI_STAGE=
11 MONGO_URI_DEV=
+# CQRS Database Configuration - During initial migration, configure command and query URIs 
+# to point to the same database. They can be separated to different servers as needed later.
12 COMMAND_MONGO_URI_PROD=
13 COMMAND_MONGO_URI_STAGE=
14 COMMAND_MONGO_URI_DEV=
15 QUERY_MONGO_URI_PROD=
16 QUERY_MONGO_URI_STAGE=
17 QUERY_MONGO_URI_DEV=
```

</blockquote></details>
<details>
<summary>src/device-registry/config/database.js (3)</summary><blockquote>

`30-41`: **Database naming convention needs improvement.**

While the command connection correctly uses a specific naming convention (`${constants.DB_NAME}_command`), the query connection just uses the base name without a suffix. For consistency and clarity, consider using a suffix for the query database as well.


```diff
const createQueryConnection = () =>
  mongoose.createConnection(QUERY_URI, {
    ...options,
-    dbName: `${constants.DB_NAME}`,
+    dbName: `${constants.DB_NAME}_query`,
  });
```

---

`49-51`: **Commented log statement for successful connections.**

You have a commented-out logger statement for successful database connections. Either uncomment it to provide visibility into successful connections or remove it entirely.


```diff
db.on("open", () => {
-  // logger.info(`Connected to ${dbType} database successfully`);
+  logger.info(`Connected to ${dbType} database successfully`);
});
```

---

`90-91`: **Database connections are initialized at module load time.**

The database connections are established immediately when the module is loaded. While this ensures connections are available, it may complicate testing and could cause issues if the module is imported but not used. Consider implementing a lazy connection approach or making this more explicit.


A more flexible approach would be to export an initialization function:

```diff
-// Initialize both database connections
-const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+// Database connection variables
+let commandMongoDB = null;
+let queryMongoDB = null;
+
+/**
+ * Initialize database connections if they haven't been established
+ */
+function initializeDatabaseConnections() {
+  if (!commandMongoDB || !queryMongoDB) {
+    const connections = connectToMongoDB();
+    commandMongoDB = connections.commandDB;
+    queryMongoDB = connections.queryDB;
+  }
+  return { commandMongoDB, queryMongoDB };
+}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 7543642a5786d6d4b73a4637cde39dcef312f636 and fdf89ce9670410f8820c1333db1b7c26bb6fa61d.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `src/device-registry/.env.example` (1 hunks)
* `src/device-registry/config/database.js` (2 hunks)
* `src/device-registry/config/environments/development.js` (1 hunks)
* `src/device-registry/config/environments/production.js` (1 hunks)
* `src/device-registry/config/environments/staging.js` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Actions: code-coverage-workflow</summary>

<details>
<summary>src/device-registry/config/database.js</summary>

[error] 10-10: TypeError: logger.error is not a function

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: build-push-deploy-device-registry
* GitHub Check: Analyze (python)
* GitHub Check: Analyze (javascript)

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>src/device-registry/config/environments/staging.js (1)</summary>

`11-12`: **Implementation of CQRS database connections looks good**

The addition of separate COMMAND_MONGO_URI and QUERY_MONGO_URI variables for the staging environment aligns perfectly with the Command Query Responsibility Segregation pattern described in the PR objectives. This clean separation will allow for independent scaling of read and write operations.

</details>
<details>
<summary>src/device-registry/config/environments/development.js (1)</summary>

`11-12`: **Good implementation of CQRS pattern for development environment**

The addition of separate COMMAND_MONGO_URI and QUERY_MONGO_URI variables for the development environment is consistent with the pattern applied to other environments. This maintains a coherent approach across all deployment contexts.

</details>
<details>
<summary>src/device-registry/config/environments/production.js (2)</summary>

`11-12`: **Production CQRS implementation looks good**

The addition of COMMAND_MONGO_URI and QUERY_MONGO_URI variables to the production environment completes the consistent implementation across all environments. This ensures that the CQRS pattern is fully supported in production.

---

`8-42`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify database connection resilience in production**

For production environments, it's especially important to ensure that there's proper fallback handling if one of the new database connections fails. The PR objectives mention "enhanced error handling," but I'd recommend verifying how connection failures are managed.

---

🏁 Script executed:

```shell
#!/bin/bash
# Look for error handling related to database connections
echo "Checking database connection error handling:"
# Search for error handling in database configuration
rg -A 10 -B 3 "catch|error|exception" --glob "**/*.js" | grep -i "mongo\|database\|connection"

# Look for any retry logic
rg -A 5 "retry|reconnect" --glob "**/*.js" | grep -i "mongo\|database\|connection"
```

Length of output: 73932

---



</details>

**Action Required: Confirm Robust Fallback Handling for Production DB Connections**  

It appears that error handling for database connections is implemented across several modules (e.g., in `src/device-registry/config/database.js` where error events trigger logging and process termination). However, the production configuration itself (in `src/device-registry/config/environments/production.js`) doesn’t include explicit fallback or reconnection logic for a failed database connection. Please verify that the current approach—relying on error handlers that log issues and exit—is acceptable for production resilience, or if additional fallback mechanisms (like automated retries or seamless failovers) should be implemented. Consider coordinating with your process manager or orchestration system to ensure graceful recovery if a connection fails.

- Review the error handling and process termination logic in `src/device-registry/config/database.js`.
- Confirm that terminating the process on errors is the desired behavior in production, or update the logic to include a fallback mechanism.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 30-30: This property value named BIGQUERY_DEVICE_UPTIME_TABLE is later overwritten by an object member with the same name.

Overwritten with this value.

If an object property with the same name is defined multiple times (except when combining a getter with a setter), only the last definition makes it into the object and previous definitions are ignored.
Unsafe fix: Remove this property value named BIGQUERY_DEVICE_UPTIME_TABLE


(lint/suspicious/noDuplicateObjectKeys)

</details>

</details>

</details>
<details>
<summary>src/device-registry/.env.example (2)</summary>

`12-17`: **Environment variables for CQRS added properly**

The addition of the six new environment variables (COMMAND_MONGO_URI and QUERY_MONGO_URI for each environment) is well-structured and placed logically near the existing MongoDB URI variables. This provides a clear template for developers to follow when setting up their environments.

---

`12-17`: <details>
<summary>❓ Verification inconclusive</summary>

**Verify other dependent services are aware of these changes**

For a complete implementation of CQRS, it's important to verify that all services that interact with the device registry are aware of these changes, especially if they directly access the database.

---

🏁 Script executed:

```shell
#!/bin/bash
# Look for imports of the configuration that might need updating
echo "Looking for imports of environment configs that might need updating:"
rg -l "require.*environments\/(development|production|staging)" --glob "**/*.js" | grep -v "environments"

# Check for direct references to MONGO_URI that might need to be updated
echo "Checking for code that might need updating to use COMMAND_MONGO_URI or QUERY_MONGO_URI:"
rg "MONGO_URI(_PROD|_STAGE|_DEV)?" --glob "**/*.js" | grep -v "environments\/"
```

Length of output: 2964

---



</details>

**Subject: Verify CQRS Config Propagation Across Dependent Services**

The device-registry (and even auth-service) now properly fall back to the new COMMAND_MONGO_URI/QUERY_MONGO_URI values. However, our grep results reveal that several other services (e.g., blog-content-manager, data-mgt, incentives, and others) continue to reference the generic MONGO_URI directly. Please confirm that these services are intended to remain on the legacy variable or that they need to be updated for full CQRS consistency. Also, note that the regex used to check environment config imports returned a parse error—this might need revisiting to avoid false negatives in your verification tests.

</details>
<details>
<summary>src/device-registry/config/database.js (5)</summary>

`12-14`: **Good fallback pattern for database URIs.**

The use of fallback patterns (`||`) for the URIs ensures backward compatibility, allowing for a smooth migration strategy as mentioned in the PR objectives. This approach enables initially configuring both URIs to point to the same database server, with the flexibility to separate them later.

---

`74-77`: **Good error handling for unhandled promises.**

Setting up global error handlers for unhandled rejections and exceptions is a best practice, especially in a database connection module where asynchronous operations are common.

---

`96-104`: **Command tenant DB implementation looks correct.**

The implementation for getting a tenant-specific command database is well-structured. It creates a unique database name with the tenant ID, uses MongoDB's `useDb` with caching for efficiency, and includes proper error handling.

---

`140-145`: **Good backward compatibility implementation.**

The backward compatible function with an optional parameter is a clean way to maintain support for existing code while enabling the new functionality. Defaulting to "query" operations is a sensible choice.

---

`151-161`: **Well-structured model retrieval with operation type parameter.**

The implementation of `getModelByTenant` with an optional operation type parameter provides a clean interface for client code to specify the database operation type. This maintains backward compatibility while enabling CQRS.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T16:12:46Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>♻️ Duplicate comments (2)</summary><blockquote>

<details>
<summary>src/device-registry/config/database.js (2)</summary><blockquote>

`7-10`: _⚠️ Potential issue_

**Ensure logger is properly configured to prevent TypeError errors.**

The pipeline failure shows "TypeError: logger.error is not a function", suggesting an issue with the logger initialization. This type of error typically occurs when log4js isn't configured before getLogger is called.




Consider refactoring the logger initialization to ensure proper configuration:

```diff
const log4js = require("log4js");
+ // Ensure log4js is configured before getting logger
+ if (!log4js.configured) {
+   log4js.configure({
+     appenders: { console: { type: "console" } },
+     categories: { default: { appenders: ["console"], level: "info" } }
+   });
+ }
const logger = log4js.getLogger(
  `${constants.ENVIRONMENT} -- dbConnection-config`
);
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: code-coverage-workflow</summary>

[error] 10-10: TypeError: logger.error is not a function

</details>

</details>

---

`111-113`: _🛠️ Refactor suggestion_

**Inconsistent DB naming convention for query tenant databases.**

There's a commented-out line showing a consistent naming pattern (`${constants.DB_NAME}_query_${tenantId}`), but the actual implementation uses a different pattern that doesn't indicate it's a query database.




```diff
function getQueryTenantDB(tenantId, modelName, schema) {
-  // const dbName = `${constants.DB_NAME}_query_${tenantId}`;
-  const dbName = `${constants.DB_NAME}_${tenantId}`;
+  const dbName = `${constants.DB_NAME}_query_${tenantId}`;
  if (queryMongoDB) {
```

This change will make it clear which databases are for queries and maintain consistent naming with the command databases.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>src/device-registry/config/database.js (2)</summary><blockquote>

`30-41`: **Review database naming convention for consistency.**

The command database uses a "_command" suffix, but the query database uses the base name without a suffix. This difference may lead to confusion and makes it harder to identify which database is which when inspecting the database server.



Consider using a consistent naming approach:

```diff
const createQueryConnection = () =>
  mongoose.createConnection(QUERY_URI, {
    ...options,
-    dbName: `${constants.DB_NAME}`,
+    dbName: `${constants.DB_NAME}_query`,
  });
```

---

`47-64`: **Improve connection event handling.**

The implementation of connection event handlers is good, but there are two minor issues:

1. The successful connection log message is commented out, which means you won't have visibility into successful connections
2. The Biome analyzer suggests using optional chaining for err.message access



```diff
const setupConnectionHandlers = (db, dbType) => {
  db.on("open", () => {
-    // logger.info(`Connected to ${dbType} database successfully`);
+    logger.info(`Connected to ${dbType} database successfully`);
  });

  db.on("error", (err) => {
    logger.error(`${dbType} database connection error: ${err.message}`);
  });

  db.on("disconnected", (err) => {
    logger.warn(
      `${dbType} database disconnected: ${
-        err && err.message ? err.message : "Unknown reason"
+        err?.message || "Unknown reason"
      }`
    );
  });
};
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 60-60: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fdf89ce9670410f8820c1333db1b7c26bb6fa61d and ef43a76811d2f2da8f25915e0b603bbcbe5396ab.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `src/device-registry/config/database.js` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>src/device-registry/config/database.js</summary>

[error] 60-60: Change to an optional chain.

Unsafe fix: Change to an optional chain.


(lint/complexity/useOptionalChain)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: code-coverage-workflow</summary>

<details>
<summary>src/device-registry/config/database.js</summary>

[error] 10-10: TypeError: logger.error is not a function

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: build-push-deploy-device-registry
* GitHub Check: Analyze (python)
* GitHub Check: Analyze (javascript)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>src/device-registry/config/database.js (4)</summary>

`12-14`: **Good implementation of fallback URIs for backward compatibility.**

The fallback chain ensures graceful degradation by first trying CQRS-specific URIs, then falling back to the legacy URI, and finally defaulting to an empty string if nothing is defined.

---

`142-147`: **Well-implemented backward compatibility in getTenantDB.**

The function elegantly routes to the appropriate specialized function based on the operation type, defaulting to query operations for backward compatibility. This is a good pattern for maintaining API compatibility while introducing new features.

---

`153-163`: **Well-implemented backward compatibility in getModelByTenant.**

Similar to getTenantDB, this function provides a smooth migration path for existing code while allowing new code to explicitly specify the operation type.

---

`165-173`: **Comprehensive module exports including both backward-compatible and specialized functions.**

The exports include both the new specialized functions and the backward-compatible functions, giving clients the flexibility to choose which API to use based on their needs.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T16:20:20Z
user: login: Baalmart
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T16:27:47Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T16:28:43Z

Review comments:
diff_hunk: @@ -0,0 +1,204 @@
+const mongoose = require("mongoose");
+const Schema = mongoose.Schema;
+const constants = require("@config/constants");
+const isEmpty = require("is-empty");
+const { getModelByTenant } = require("@config/database");
+const httpStatus = require("http-status");
+const { HttpError, logObject } = require("@utils/shared");
+const log4js = require("log4js");
+const logger = log4js.getLogger(
+  `${constants.ENVIRONMENT} -- tenant-settings-model`
+);
+
+const TenantSettingsSchema = new Schema({
+  tenant: { type: String, required: true, unique: true },
+  defaultNetwork: { type: Schema.Types.ObjectId, ref: "Network" },
+  defaultNetworkRole: { type: Schema.Types.ObjectId, ref: "Role" },
+  defaultGroup: { type: Schema.Types.ObjectId, ref: "Group" },
+  defaultGroupRole: { type: Schema.Types.ObjectId, ref: "Role" },
+});
+TenantSettingsSchema.statics = {
+  async register(args, next) {
+    try {
+      const createdTenantSettings = await this.create({
+        ...args,
+      });
+      if (!isEmpty(createdTenantSettings)) {
+        return {
+          success: true,
+          data: createdTenantSettings,
+          message: "tenant settings created",
+          status: httpStatus.OK,
+        };
+      } else if (isEmpty(createdTenantSettings)) {
+        return {
+          success: true,
+          data: createdTenantSettings,
+          message:
+            "Operation successful but tenant settings NOT successfully created",
+          status: httpStatus.OK,
+        };
+      }
+    } catch (err) {
+      logObject("the error", err);
+      let response = {};
+      let message = "validation errors for some of the provided fields";
+      let status = httpStatus.CONFLICT;
+      if (err.code === 11000) {
+        logObject("the err.code again", err.code);
+        const duplicate_record = args.tenant ? args.tenant : "unknown";
+        response[duplicate_record] = `${duplicate_record} must be unique`;
+        response["message"] =
+          "the tenant name must be unique for every tenant setting";
+      } else if (err.keyValue) {
+        Object.entries(err.keyValue).forEach(([key, value]) => {
+          return (response[key] = `the ${key} must be unique`);
+        });
+      } else if (err.errors) {
+        Object.entries(err.errors).forEach(([key, value]) => {
+          return (response[key] = value.message);
+        });
+      }
+      logger.error(`🐛🐛 Internal Server Error -- ${err.message}`);
+      next(new HttpError(message, status, response));
+    }
+  },
+  async list({ skip = 0, limit = 100, filter = {} } = {}, next) {
+    try {
+      const totalCount = await this.countDocuments(filter).exec();
+      const data = await this.find(filter)
+        .sort({ createdAt: -1 })
+        .skip(skip ? parseInt(skip) : 0)
+        .limit(limit ? parseInt(limit) : parseInt(constants.DEFAULT_LIMIT))
+        .exec();
+      if (!isEmpty(data)) {
+        return {
+          success: true,
+          message: "successfully retrieved the tenant settings",
+          data,
+          totalCount,
+          status: httpStatus.OK,
+        };
+      } else if (isEmpty(data)) {
+        return {
+          success: true,
+          message: "no tenant settings exist",
+          data: [],
+          totalCount,
+          status: httpStatus.OK,
+        };
+      }
+    } catch (error) {
+      logger.error(`🐛🐛 Internal Server Error -- ${error.message}`);
+      if (error instanceof HttpError) {
+        return next(error);
+      }
+      return next(
+        new HttpError(
+          "Internal Server Error",
+          httpStatus.INTERNAL_SERVER_ERROR,
+          { message: error.message }
+        )
+      );
+    }
+  },
path: src/auth-service/models/TenantSettings.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**list method provides useful pagination, but large skip/limit usage may impact performance.**  
Consider implementing a more scalable pagination approach (e.g., using an index-based or cursor-based technique) for collections with large data sets to avoid resource-intensive operations.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-02T08:58:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,15 @@ mongoose.set("useFindAndModify", false);
 mongoose.set("useCreateIndex", true);
 mongoose.set("debug", false);
 const constants = require("./constants");
-const URI = constants.MONGO_URI;
 const log4js = require("log4js");
 const logger = log4js.getLogger(
   `${constants.ENVIRONMENT} -- dbConnection-config`
 );
 
+// Database URIs for CQRS pattern
+const COMMAND_URI = constants.COMMAND_MONGO_URI || constants.MONGO_URI;
+const QUERY_URI = constants.QUERY_MONGO_URI || constants.MONGO_URI;
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Ensure environment variables are properly set**  
The pipeline error indicates that `COMMAND_MONGO_URI` or `QUERY_MONGO_URI` may be `undefined`. Double-check that the environment or constants file sets `COMMAND_MONGO_URI` and `QUERY_MONGO_URI`.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-02T08:58:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,6 +14,9 @@ const {
   extractErrorsFromRequest,
 } = require("@utils/shared");
 
+const COMMAND_URI = constants.COMMAND_MONGO_URI || constants.MONGO_URI;
+const QUERY_URI = constants.QUERY_MONGO_URI || constants.MONGO_URI;
path: src/auth-service/config/database.js
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Validate environment variables**  
Like the device-registry config, ensure `COMMAND_MONGO_URI` and `QUERY_MONGO_URI` are well-defined. The pipeline logs warn about `undefined` URIs.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-02T08:58:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -26,67 +28,142 @@
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection instances for command and query
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_query`,
+  });
+
+// Connection storage
+let commandDB = null;
+let queryDB = null;
+
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnection", (err) => {
+    logger.warn(`${dbType} database disconnected`);
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
+    // Connect to command database
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("error", (err) => {
-      // process.exit(0);
-    });
+    // Connect to query database
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    db.on("disconnection", (err) => {});
-
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
-      // process.exit(0);
-      // db.close(() => {
-      //   process.exit(0);
-      // });
+    // Error handling for the Node process
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
-      // process.exit(1);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
+    return { commandDB, queryDB };
   } catch (error) {
-    // logger.error(`🐛🐛 Internal Server Error -- ${error.message}`);
+    logger.error(`🐛🐛 Internal Server Error -- ${error.message}`);
+    throw error;
   }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+
+/**
+ * Get a tenant-specific database from command DB (for write operations)
+ */
+function getCommandTenantDB(tenantId, modelName, schema) {
+  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
+  if (commandMongoDB) {
+    const db = commandMongoDB.useDb(dbName, { useCache: true });
+    db.model(modelName, schema);
+    return db;
+  }
+  throw new Error("Command database connection not established");
+}
 
-/****
- * creating a new mongoDB connection by switching tenant
- * using this to create a new connection based on tenant ID
+/**
+ * Get a tenant-specific database from query DB (for read operations)
  */
-function getTenantDB(tenantId, modelName, schema) {
-  const dbName = `${constants.DB_NAME}_${tenantId}`;
-  if (mongodb) {
-    const db = mongodb.useDb(dbName, { useCache: true });
+function getQueryTenantDB(tenantId, modelName, schema) {
+  const dbName = `${constants.DB_NAME}_query_${tenantId}`;
+  if (queryMongoDB) {
+    const db = queryMongoDB.useDb(dbName, { useCache: true });
     db.model(modelName, schema);
     return db;
   }
+  throw new Error("Query database connection not established");
+}
path: src/auth-service/config/database.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Segregating tenant DB for command/query**  
Please confirm that each tenant’s read and write DB usage is consistent across all new controllers and utilities. This ensures data flows remain correct under CQRS.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-02T08:58:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,12 +4,15 @@ mongoose.set("useFindAndModify", false);
 mongoose.set("useCreateIndex", true);
 mongoose.set("debug", false);
 const constants = require("./constants");
-const URI = constants.MONGO_URI;
 const log4js = require("log4js");
 const logger = log4js.getLogger(
   `${constants.ENVIRONMENT} -- dbConnection-config`
 );
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Logger initialization might be incorrect.**

The pipeline error "TypeError: logger.error is not a function" suggests an issue with the logger setup. Ensure that log4js is configured correctly and that the environment variable needed for the logger name is properly set.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check the constants file for ENVIRONMENT variable
grep -n "ENVIRONMENT" src/device-registry/config/constants.js

# Check if there's a log4js configuration somewhere
grep -r -n "log4js.configure" src/device-registry/
```

Length of output: 274

---



</details>

**Logger Configuration Issue – Action Required**

The investigation shows that the `log4js.configure` call is only executed in the bin entry points (e.g., in `src/device-registry/bin/index.js` and `src/device-registry/bin/test/ut_index.js`), while the logger in `src/device-registry/config/database.js` is created independently. This means that if the module is imported in contexts where the bin files don’t run, the logger instance may lack essential methods (like `error`), hence triggering the error.

- Verify that the `ENVIRONMENT` variable from `src/device-registry/config/constants.js` is properly set.
- Ensure that `log4js.configure` is executed before any module (like `database.js`) calls `log4js.getLogger`. Consider centralizing the configuration in an initialization script that runs for all contexts.

Please adjust the initialization flow so that the logger is correctly configured across all parts of the application.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: code-coverage-workflow</summary>

[error] 10-10: TypeError: logger.error is not a function

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T16:12:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,147 @@
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${err ? err.message : "Unknown reason"}`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+
+/**
+ * Get a tenant-specific command database (for write operations)
+ */
+function getCommandTenantDB(tenantId, modelName, schema) {
+  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
+  if (commandMongoDB) {
+    const db = commandMongoDB.useDb(dbName, { useCache: true });
+    db.model(modelName, schema);
+    return db;
+  }
+  throw new Error("Command database connection not established");
+}
 
-function getTenantDB(tenantId, modelName, schema) {
+/**
+ * Get a tenant-specific query database (for read operations)
+ */
+function getQueryTenantDB(tenantId, modelName, schema) {
+  // const dbName = `${constants.DB_NAME}_query_${tenantId}`;
   const dbName = `${constants.DB_NAME}_${tenantId}`;
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Commented code and inconsistent naming convention in query tenant DB.**

There's a commented-out line that defines a database name with a `_query_` suffix, but the actual implementation just uses the tenant ID without a query indication. This inconsistency should be addressed.


```diff
function getQueryTenantDB(tenantId, modelName, schema) {
-  // const dbName = `${constants.DB_NAME}_query_${tenantId}`;
-  const dbName = `${constants.DB_NAME}_${tenantId}`;
+  const dbName = `${constants.DB_NAME}_query_${tenantId}`;
  if (queryMongoDB) {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
function getQueryTenantDB(tenantId, modelName, schema) {
  const dbName = `${constants.DB_NAME}_query_${tenantId}`;
  if (queryMongoDB) {
    // rest of implementation...
  }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T16:12:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,147 @@
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${err ? err.message : "Unknown reason"}`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+
+/**
+ * Get a tenant-specific command database (for write operations)
+ */
+function getCommandTenantDB(tenantId, modelName, schema) {
+  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
+  if (commandMongoDB) {
+    const db = commandMongoDB.useDb(dbName, { useCache: true });
+    db.model(modelName, schema);
+    return db;
+  }
+  throw new Error("Command database connection not established");
+}
 
-function getTenantDB(tenantId, modelName, schema) {
+/**
+ * Get a tenant-specific query database (for read operations)
+ */
+function getQueryTenantDB(tenantId, modelName, schema) {
+  // const dbName = `${constants.DB_NAME}_query_${tenantId}`;
   const dbName = `${constants.DB_NAME}_${tenantId}`;
-  if (mongodb) {
-    const db = mongodb.useDb(dbName, { useCache: true });
+  if (queryMongoDB) {
+    const db = queryMongoDB.useDb(dbName, { useCache: true });
     db.model(modelName, schema);
     return db;
   }
+  throw new Error("Query database connection not established");
+}
+
+/**
+ * Get a command model (for write operations) for a specific tenant
+ */
+function getCommandModelByTenant(tenantId, modelName, schema) {
+  const tenantDb = getCommandTenantDB(tenantId, modelName, schema);
+  return tenantDb.model(modelName);
 }
 
-function getModelByTenant(tenantId, modelName, schema) {
-  const tenantDb = getTenantDB(tenantId, modelName, schema);
+/**
+ * Get a query model (for read operations) for a specific tenant
+ */
+function getQueryModelByTenant(tenantId, modelName, schema) {
+  const tenantDb = getQueryTenantDB(tenantId, modelName, schema);
   return tenantDb.model(modelName);
 }
 
-module.exports = { getModelByTenant, getTenantDB, connectToMongoDB };
+/**
+ * Backward compatible function to get tenant database
+ * Now accepts an optional operationType parameter
+ */
+function getTenantDB(tenantId, modelName, schema, operationType = "query") {
+  if (operationType === "command") {
+    return getCommandTenantDB(tenantId, modelName, schema);
+  }
+  return getQueryTenantDB(tenantId, modelName, schema);
+}
+
+/**
+ * Backward compatible function to get tenant model
+ * Now accepts an optional operationType parameter
+ */
+function getModelByTenant(
+  tenantId,
+  modelName,
+  schema,
+  operationType = "query"
+) {
+  if (operationType === "command") {
+    return getCommandModelByTenant(tenantId, modelName, schema);
+  }
+  return getQueryModelByTenant(tenantId, modelName, schema);
+}
+
+module.exports = {
+  getModelByTenant,
+  getTenantDB,
+  connectToMongoDB,
+  getCommandModelByTenant,
+  getQueryModelByTenant,
+  getCommandTenantDB,
+  getQueryTenantDB,
+};
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Pipeline failure related to logger.**

The pipeline is reporting "TypeError: logger.error is not a function" which suggests an issue with the logger configuration. The error seems related to line 10-10 which is within the logger initialization.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check the logger initialization
grep -n "logger =" src/device-registry/config/*.js

# Check if log4js is correctly imported in all files
grep -n "log4js" src/device-registry/config/*.js
```

Length of output: 927

---



</details>

**Logger Import Conflict: Revise Local Log4js Implementation**

It appears that the local file `src/device-registry/config/log4js.js` is inadvertently shadowing the intended npm package since the logger is imported with `require("log4js")` in `src/device-registry/config/database.js`. The custom file does not expose a proper `getLogger` method, so when the code calls `logger.error`, it fails.

Please consider one of the following corrective actions:
- **Rename the local log4js file** (or update its location) to prevent naming conflicts with the npm package.
- **Or update the require statement** in `src/device-registry/config/database.js` to explicitly import the correct module (e.g., using a relative path like `require("./log4js")` if that is intended).

These changes should help ensure that the logger is correctly configured, resolving the pipeline failure.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T16:12:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,147 @@
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${err ? err.message : "Unknown reason"}`
+    );
+  });
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Potential logging error in disconnection event handler.**

The disconnection event may not provide an error object, which could cause an issue when trying to access `err.message`. Your code includes a fallback message, but the check should be on whether `err` exists, not on the presence of an error message.


```diff
db.on("disconnected", (err) => {
  logger.warn(
-    `${dbType} database disconnected: ${err ? err.message : "Unknown reason"}`
+    `${dbType} database disconnected: ${err && err.message ? err.message : "Unknown reason"}`
  );
});
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
  db.on("disconnected", (err) => {
    logger.warn(
      `${dbType} database disconnected: ${err && err.message ? err.message : "Unknown reason"}`
    );
  });
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-03T16:12:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,149 @@ const options = {
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${
+        err && err.message ? err.message : "Unknown reason"
+      }`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Immediate connection invocation may cause issues with module imports.**

Initializing the database connections immediately when the module is loaded could cause problems in testing scenarios or when the module is imported but not yet ready to connect to the database.



Consider making the connections lazy-loaded or providing a way to initialize connections explicitly:

```diff
- // Initialize both database connections
- const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+ // Database connection instances
+ let commandMongoDB = null;
+ let queryMongoDB = null;
+ 
+ // Initialize database connections explicitly
+ function initializeConnections() {
+   if (!commandMongoDB || !queryMongoDB) {
+     const connections = connectToMongoDB();
+     commandMongoDB = connections.commandDB;
+     queryMongoDB = connections.queryDB;
+   }
+   return { commandMongoDB, queryMongoDB };
+ }
```

Then update each DB access function to call initializeConnections() if needed:

```diff
function getCommandTenantDB(tenantId, modelName, schema) {
  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
+  if (!commandMongoDB) {
+    initializeConnections();
+  }
  if (commandMongoDB) {
    // rest of function
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **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.

`````suggestion
// Instead of immediately initializing the connections:
// const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();

// Use lazy-loaded database connection instances
let commandMongoDB = null;
let queryMongoDB = null;

// Initialize database connections explicitly
function initializeConnections() {
  if (!commandMongoDB || !queryMongoDB) {
    const connections = connectToMongoDB();
    commandMongoDB = connections.commandDB;
    queryMongoDB = connections.queryDB;
  }
  return { commandMongoDB, queryMongoDB };
}

// Example usage in a DB access function:
function getCommandTenantDB(tenantId, modelName, schema) {
  const dbName = `${constants.DB_NAME}_command_${tenantId}`;
  if (!commandMongoDB) {
    initializeConnections();
  }
  if (commandMongoDB) {
    // rest of function implementation using commandMongoDB
  }
  // Possibly add handling for the case when commandMongoDB is still null
}

// Similar updates should be applied for functions using queryMongoDB.
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T16:20:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,149 @@ const options = {
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${
+        err && err.message ? err.message : "Unknown reason"
+      }`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
path: src/device-registry/config/database.js
user: login: Baalmart
body: create a Github issue for this.
created_at: 2025-03-03T16:27:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -22,45 +25,149 @@ const options = {
   connectTimeoutMS: 1200000,
   socketTimeoutMS: 600000,
   serverSelectionTimeoutMS: 3600000,
-  dbName: constants.DB_NAME,
 };
 
-const connect = () => mongoose.createConnection(URI, options);
+// Create separate connection functions for command and query databases
+const createCommandConnection = () =>
+  mongoose.createConnection(COMMAND_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}_command`,
+  });
+
+const createQueryConnection = () =>
+  mongoose.createConnection(QUERY_URI, {
+    ...options,
+    dbName: `${constants.DB_NAME}`,
+  });
+
+// Store database connections
+let commandDB = null;
+let queryDB = null;
+
+// Helper function to set up connection event handlers
+const setupConnectionHandlers = (db, dbType) => {
+  db.on("open", () => {
+    // logger.info(`Connected to ${dbType} database successfully`);
+  });
+
+  db.on("error", (err) => {
+    logger.error(`${dbType} database connection error: ${err.message}`);
+  });
+
+  db.on("disconnected", (err) => {
+    logger.warn(
+      `${dbType} database disconnected: ${
+        err && err.message ? err.message : "Unknown reason"
+      }`
+    );
+  });
+};
 
 const connectToMongoDB = () => {
   try {
-    const db = connect();
-    db.on("open", () => {});
-    db.on("error", (err) => {});
+    // Establish command database connection
+    commandDB = createCommandConnection();
+    setupConnectionHandlers(commandDB, "command");
 
-    db.on("disconnection", (err) => {});
+    // Establish query database connection
+    queryDB = createQueryConnection();
+    setupConnectionHandlers(queryDB, "query");
 
-    process.on("unlimitedRejection", (reason, p) => {
-      console.log("Unhandled Rejection at: Promise", p, "reason:", reason);
+    // Set up global error handlers
+    process.on("unhandledRejection", (reason, p) => {
+      logger.error("Unhandled Rejection at: Promise", p, "reason:", reason);
     });
 
     process.on("uncaughtException", (err) => {
-      console.error("There was an uncaught error", err);
+      logger.error("There was an uncaught error", err);
     });
 
-    return db;
-  } catch (error) {}
+    return { commandDB, queryDB };
+  } catch (error) {
+    logger.error(`Database connection error: ${error.message}`);
+    throw error;
+  }
 };
 
-const mongodb = connectToMongoDB();
+// Initialize both database connections
+const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
path: src/device-registry/config/database.js
user: login: coderabbitai[bot]
body: <details>
<summary>🧩 Analysis chain</summary>

@Baalmart I've created a GitHub issue for the database connection refactoring suggestion. The issue addresses the concern about immediate connection invocation potentially causing problems in testing scenarios or when the module is imported but not ready to connect to the database.

The issue proposes refactoring the connections to use a lazy-loading pattern, which will improve testability and provide more explicit control over when database connections are established. It includes the specific code changes needed and has been assigned to you.

You can find the issue at the URL provided in the GitHub CLI response above.

---

🏁 Script executed:
```
#!/bin/bash
# Create a GitHub issue for the database connection refactoring

gh issue create \
  --title "[Device Registry] Refactor database connections to lazy loading pattern" \
  --body "## Background

In PR #4513 (https://github.com/airqo-platform/AirQo-api/pull/4513), the following refactor suggestion was identified:

**Immediate connection invocation may cause issues with module imports.**

Initializing the database connections immediately when the module is loaded could cause problems in testing scenarios or when the module is imported but not yet ready to connect to the database.

## Proposed Changes

Refactor the database connections in \`src/device-registry/config/database.js\` to use lazy-loading pattern:

\`\`\`diff
- // Initialize both database connections
- const { commandDB: commandMongoDB, queryDB: queryMongoDB } = connectToMongoDB();
+ // Database connection instances
+ let commandMongoDB = null;
+ let queryMongoDB = null;
+ 
+ // Initialize database connections explicitly
+ function initializeConnections() {
+   if (!commandMongoDB || !queryMongoDB) {
+     const connections = connectToMongoDB();
+     commandMongoDB = connections.commandDB;
+     queryMongoDB = connections.queryDB;
+   }
+   return { commandMongoDB, queryMongoDB };
+ }
\`\`\`

Then update each DB access function to call initializeConnections() if needed:

\`\`\`diff
function getCommandTenantDB(tenantId, modelName, schema) {
  const dbName = \`\${constants.DB_NAME}_command_\${tenantId}\`;
+  if (!commandMongoDB) {
+    initializeConnections();
+  }
  if (commandMongoDB) {
    // rest of function
\`\`\`

## Benefits
- Improves testability
- Avoids issues when the module is imported but not yet ready to connect to the database
- Makes database connections lazy-loaded

## Related PR
This issue was referenced in PR #4513 (https://github.com/airqo-platform/AirQo-api/pull/4513#discussion_r1977804219)" \
  --assignee "Baalmart"
```

Length of output: 1888

---



</details>

@Baalmart I've successfully created a GitHub issue for the database connection refactoring suggestion. The issue has been created and assigned to you:

Issue: [#4520](https://github.com/airqo-platform/AirQo-api/issues/4520)

The issue includes:
- The background explaining why immediate connection invocation could be problematic
- The proposed code changes to implement lazy loading
- The benefits of this approach (improved testability, avoiding issues with module imports)
- A reference back to this PR discussion

This will allow you to address this refactoring in a separate PR while proceeding with the current implementation.

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Great%20advice%20from%20%40coderabbitai!%20%F0%9F%92%A1%20Lazy-loading%20database%20connections%20can%20prevent%20issues%20during%20module%20imports%20and%20testing.%20Optimize%20your%20code%20for%20better%20reliability!%20%F0%9F%9B%A0%EF%B8%8F&url=https%3A%2F%2Fgithub.com%2Fairqo-platform%2FAirQo-api%2Fpull%2F4513%23discussion_r1977804219)?
							Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-03-03T16:28:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-02T08:55:32Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

## Walkthrough
This pull request implements a Command Query Responsibility Segregation (CQRS) pattern in the device registry module. The database connection logic now distinguishes between command and query operations by creating separate connections, functions, and tenant-specific retrieval mechanisms. Additionally, environment configurations across development, staging, and production have been updated to support distinct command and query database URIs, with enhanced error handling and logging.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `src/device-registry/config/database.js` | Implemented CQRS by replacing a single connection with `createCommandConnection` and `createQueryConnection`; added new tenant DB retrieval functions (`getCommandTenantDB`, `getQueryTenantDB`, `getCommandModelByTenant`, `getQueryModelByTenant`); updated `getTenantDB` and `getModelByTenant` signatures with an `operationType` parameter. |
| `src/device-registry/.env.example` | Added six new environment variables for command and query MongoDB URIs across production, staging, and development. |
| `src/device-registry/config/environments/development.js` | Added `COMMAND_MONGO_URI` and `QUERY_MONGO_URI` properties in development configuration pointing to the corresponding environment variables. |
| `src/device-registry/config/environments/production.js` | Added `COMMAND_MONGO_URI` and `QUERY_MONGO_URI` properties in production configuration pointing to the corresponding environment variables. |
| `src/device-registry/config/environments/staging.js` | Added `COMMAND_MONGO_URI` and `QUERY_MONGO_URI` properties in staging configuration pointing to the corresponding environment variables. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant App as Application
    participant DB as Database Module
    participant CMD as Command Connection
    participant QRY as Query Connection

    App->>DB: connectToMongoDB()
    DB->>CMD: createCommandConnection(COMMAND_URI)
    DB->>QRY: createQueryConnection(QUERY_URI)
    CMD-->>DB: Connection status (open/error/disconnection)
    QRY-->>DB: Connection status (open/error/disconnection)
    DB-->>App: Returns commandDB & queryDB
```

```mermaid
sequenceDiagram
    participant App as Application
    participant DB as Database Module

    App->>DB: getTenantDB(tenantId, modelName, schema, operationType)
    alt operationType = "command"
        DB->>DB: getCommandTenantDB(tenantId, modelName, schema)
    else operationType = "query"
        DB->>DB: getQueryTenantDB(tenantId, modelName, schema)
    end
    DB-->>App: Returns tenant-specific DB instance
```

## Possibly related PRs
- **airqo-platform/AirQo-api#4512**: Implements a CQRS pattern with modifications to database connection methods similar to those introduced in this PR.

## Poem
> In lines of code, a new path is unfurled,  
> Command and query dance in a segregated world.  
> Connections split with logic so keen,  
> Environments set for every scene.  
> A clever rhythm of data sings—code dreams! 🎶  
> Happy coding, where structure gleams.

</details>

<!-- walkthrough_end -->

<!-- This is an auto-generated comment: resource warnings by coderabbit.ai -->

> [!WARNING]
> ## Review ran into problems
> 
> <details>
> <summary>🔥 Problems</summary>
> 
> Errors were encountered while retrieving linked issues.
> 
> <details>
> <summary>Errors (1)</summary>
> 
> * JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.
> 
> </details>
> 
> </details>

<!-- end of auto-generated comment: resource warnings by coderabbit.ai -->
<!-- internal state start -->


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

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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?

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai):

- 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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-03-02T08:56:32Z
body: ## [Codecov](https://app.codecov.io/gh/airqo-platform/AirQo-api/pull/4513?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 11.57%. Comparing base [(`4558b48`)](https://app.codecov.io/gh/airqo-platform/AirQo-api/commit/4558b48354801bc7a82ec94ecc1ea37d212492c3?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform) to head [(`ef43a76`)](https://app.codecov.io/gh/airqo-platform/AirQo-api/commit/ef43a76811d2f2da8f25915e0b603bbcbe5396ab?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform).
> Report is 6 commits behind head on staging.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/airqo-platform/AirQo-api/pull/4513/graphs/tree.svg?width=650&height=150&src=pr&token=HHq3qS3cL6&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform)](https://app.codecov.io/gh/airqo-platform/AirQo-api/pull/4513?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform)

```diff
@@             Coverage Diff             @@
##           staging    #4513      +/-   ##
===========================================
- Coverage    11.59%   11.57%   -0.02%     
===========================================
  Files          163      159       -4     
  Lines        18766    18733      -33     
  Branches       547      547              
===========================================
- Hits          2175     2169       -6     
+ Misses       16587    16560      -27     
  Partials         4        4              
```

[see 4 files with indirect coverage changes](https://app.codecov.io/gh/airqo-platform/AirQo-api/pull/4513/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=airqo-platform)

</details>
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-02T08:57:45Z
body: Auth-service changes in this PR available for preview [here](https://cqrs-device-registry-auth-service-preview-w7kzhvlewq-ew.a.run.app)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-02T08:57:58Z
body: Device registry changes in this PR available for preview [here](https://cqrs-device-registry-device-registry-preview-w7kzhvlewq-ew.a.run.app)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-03T15:45:17Z
body: Device registry changes in this PR available for preview [here](https://cqrs-device-registry-device-registry-preview-w7kzhvlewq-ew.a.run.app)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-03T16:11:48Z
body: Device registry changes in this PR available for preview [here](https://cqrs-device-registry-device-registry-preview-w7kzhvlewq-ew.a.run.app)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-03T16:19:29Z
body: Device registry changes in this PR available for preview [here](https://cqrs-device-registry-device-registry-preview-w7kzhvlewq-ew.a.run.app)
author_association: CONTRIBUTOR