Skip to content

Feature/edit collection - #364

Merged
yash-pouranik merged 7 commits into
mainfrom
feature/edit-collection
Jul 24, 2026
Merged

Feature/edit collection#364
yash-pouranik merged 7 commits into
mainfrom
feature/edit-collection

Conversation

@yash-pouranik

@yash-pouranik yash-pouranik commented Jul 24, 2026

Copy link
Copy Markdown
Member

EDIT COLLECTION FEATURE

Summary by CodeRabbit

  • New Features
    • Added a protected “Edit Schema” page for creating/updating collection fields (including nested structures and references), with a dedicated Users schema flow and confirmation.
    • Added dashboard UI entry points (button + route) and a new API endpoint to update collection schemas.
  • Bug Fixes
    • Improved CORS preflight caching (maxAge).
    • Enhanced unique-index handling during schema updates.
  • Documentation
    • Added a request-flow sequence diagram for dynamic connections and collection operations.
  • Tests
    • Added server tests for collection schema update validation and success/error cases.

@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ur-backend-web-dashboard Ready Ready Preview, Comment Jul 24, 2026 8:01pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
urbackend Skipped Skipped Jul 24, 2026 8:01pm

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@yash-pouranik, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 31 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bb05f46e-9b00-49f2-9818-bd9a3c550144

📥 Commits

Reviewing files that changed from the base of the PR and between a77cdce and 282c5ab.

📒 Files selected for processing (2)
  • apps/dashboard-api/src/controllers/project.controller.js
  • packages/common/src/utils/injectModel.js
📝 Walkthrough

Walkthrough

Adds end-to-end collection schema editing with shared validation, a protected API endpoint, dashboard editor UI, users-schema safeguards, persistence, cache refresh, events, index synchronization, and tests. Also updates CORS preflight caching and documents dynamic connection handling.

Changes

Collection schema editing

Layer / File(s) Summary
Collection update API
packages/common/src/utils/input.validation.js, packages/common/src/index.js, apps/dashboard-api/src/controllers/project.controller.js, apps/dashboard-api/src/routes/projects.js, packages/common/src/utils/injectModel.js, apps/dashboard-api/src/__tests__/*
Adds collection-edit validation, the protected update route, controller behavior, unique-index cleanup, cache and event handling, and success/error coverage.
Dashboard schema editor
apps/web-dashboard/src/App.jsx, apps/web-dashboard/src/pages/EditCollection.jsx, apps/web-dashboard/src/pages/Database.jsx, apps/web-dashboard/src/components/Database/DatabaseHeader.jsx, apps/web-dashboard/src/pages/Auth.jsx, apps/web-dashboard/src/pages/ConfirmationModal.jsx
Adds nested schema editing, payload cleanup and submission, navigation, edit controls, and confirmation handling for the users collection.

CORS preflight caching

Layer / File(s) Summary
CORS middleware configuration
apps/dashboard-api/src/app.js
Sets the CORS preflight cache duration to 86400 seconds.

Dynamic connection flow documentation

Layer / File(s) Summary
Connection lifecycle reference
dynamic_connection_sequence_diagram.md
Documents request middleware, connection resolution, model injection, query execution, collection naming, garbage collection, and related implementation files.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant EditCollection
  participant ProjectsRouter
  participant updateCollection
  participant Project
  participant CacheHelpers
  participant emitEvent
  User->>EditCollection: Edit and submit collection schema
  EditCollection->>ProjectsRouter: PUT project collection schema
  ProjectsRouter->>updateCollection: Apply authorization middleware
  updateCollection->>Project: Validate input and load project
  updateCollection->>Project: Update model and save project
  updateCollection->>CacheHelpers: Refresh project cache
  updateCollection->>emitEvent: Emit collection_updated
  updateCollection-->>EditCollection: Return sanitized project
Loading

Possibly related PRs

Suggested labels: feature, type:feature

Suggested reviewers: nitin-kumar-yadav1307, mdtowfikomer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change by indicating an edit collection feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/edit-collection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 962-993: The endpoint response paths around project collection
updates must use the mandated { success, data, message } envelope consistently.
Update the project/collection not-found, users-schema validation, success, and
catch responses in this controller method, replacing raw err.message exposure
with the established AppError and sanitized error-handling pattern while
preserving the existing status codes and payload meaning.
- Around line 950-976: Prevent editCollection from overwriting an existing
collection model when the optional schema is omitted. Update the assignment
after the users-schema validation to replace
project.collections[collectionIndex].model only when schema is defined,
preserving the stored model for requests without a schema.
- Around line 980-986: Update the project cache refresh logic around
deleteProjectById and setProjectById in updateCollection to also invalidate both
API-key caches using deleteProjectByApiKeyCache for the project’s publishableKey
and secretKey, matching createCollection behavior. Perform invalidation before
or alongside repopulating the project-by-id cache while preserving the existing
sanitized project object handling.
- Around line 976-978: Update updateCollection after assigning
project.collections[collectionIndex].model and before/around project.save to
recompile the edited collection’s model and invoke createUniqueIndexes, matching
createCollection’s behavior. Ensure unique indexes are created, changed, or
dropped to reflect additions, removals, and modifications to the collection
schema.

In `@apps/web-dashboard/src/pages/Auth.jsx`:
- Around line 553-564: The ConfirmationModal usage for
isEditUsersSchemaModalOpen shows a misleading hardcoded Delete action. Update
ConfirmationModal to accept configurable confirm-label and styling/variant
props, then use them in this editing-schema modal to display an appropriate
non-delete label and button style while preserving the existing confirmation and
cancellation behavior.

In `@apps/web-dashboard/src/pages/EditCollection.jsx`:
- Around line 576-588: The editable Name field conflicts with handleSubmit,
which only submits schema and retains the original collectionName. Either make
the Name input non-editable for all edit flows, or update handleSubmit and the
corresponding API handling to explicitly submit and apply the renamed name while
preserving the existing users restriction.
- Around line 479-486: Update the local mapFields function in the
existingCollection model-loading flow to recursively map both f.fields and
array-item sub-fields under f.items.fields, generating a fresh _id for every
nested field. Preserve the current object-nesting behavior and ensure the
transformed array item structure remains intact for FieldRow keys.

In `@dynamic_connection_sequence_diagram.md`:
- Line 216: Update the In-Memory Registry row’s idle-duration text from “20min”
to the standard spaced form “20 min” or “20 minutes,” preserving the rest of the
documentation unchanged.
- Around line 214-218: Update the Tier 3 row in the caching sequence table so
the “What’s Cached” column no longer describes raw encrypted configuration as
cached data; rename the column to “Source/operation” or describe Tier 3 as the
main MongoDB lookup and decryption source-of-truth fallback.
- Around line 238-253: Update the links in the “Key Files” table to use
repository-relative paths instead of machine-specific file:/// URLs. Preserve
each referenced file and its role while removing the local drive prefix.
- Around line 196-198: Update the PHASE 6 response example in the sequence
diagram to include the required message field alongside success and data, using
the documented empty-string message value so the canonical shape is { success,
data, message }.
- Around line 84-92: Update the POST Request (Write) authorization notes to
state that secret-key writes bypass authorization immediately, while valid
Bearer-token and ownership checks apply only to publishable-key writes. Clarify
that ownership validation and auto-injection use the configured ownerField
rather than always userId.
- Around line 54-64: The diagram’s cache lookup flow should distinguish key
types: show publishable-key verification trying the raw key first and falling
back to the hashed key, while secret-key verification uses the hashed key
directly. Update the Redis lookup notation and surrounding flow near “Check
project cache” without changing the documented database fallback behavior.
- Around line 229-232: Specify the fenced code block language for the database
naming examples by adding the `text` identifier to the existing fence, without
changing the example content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1510a1f4-f7eb-4dff-a579-75f9f9fc0abc

📥 Commits

Reviewing files that changed from the base of the PR and between c97ffea and 15a5865.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • apps/dashboard-api/src/__tests__/project.controller.editCollection.test.js
  • apps/dashboard-api/src/__tests__/routes.projects.storage.test.js
  • apps/dashboard-api/src/app.js
  • apps/dashboard-api/src/controllers/project.controller.js
  • apps/dashboard-api/src/routes/projects.js
  • apps/web-dashboard/src/App.jsx
  • apps/web-dashboard/src/components/Database/DatabaseHeader.jsx
  • apps/web-dashboard/src/pages/Auth.jsx
  • apps/web-dashboard/src/pages/Database.jsx
  • apps/web-dashboard/src/pages/EditCollection.jsx
  • dynamic_connection_sequence_diagram.md
  • packages/common/src/index.js
  • packages/common/src/utils/input.validation.js

Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
Comment thread apps/web-dashboard/src/pages/Auth.jsx
Comment thread dynamic_connection_sequence_diagram.md Outdated
Comment thread dynamic_connection_sequence_diagram.md Outdated
Comment thread dynamic_connection_sequence_diagram.md Outdated
Comment thread dynamic_connection_sequence_diagram.md Outdated
Comment thread dynamic_connection_sequence_diagram.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/common/src/index.js (1)

95-95: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Export editCollectionSchema, not syncSchemaPayload.

The supplied validation module defines editCollectionSchema, and project.controller.js invokes editCollectionSchema.parse(...). Re-exporting syncSchemaPayload leaves the controller’s expected schema unavailable and can break the collection update endpoint at runtime.

Proposed fix
-  syncSchemaPayload,
+  editCollectionSchema,

Apply the same rename in both the destructuring import and module.exports.

Also applies to: 182-182

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/common/src/index.js` at line 95, Replace syncSchemaPayload with
editCollectionSchema in both the validation-module destructuring import and
module.exports in the common index module, so the controller’s
editCollectionSchema.parse call receives the expected schema.
🧹 Nitpick comments (1)
apps/dashboard-api/src/controllers/project.controller.js (1)

979-997: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Connection lookup / model recompile / index creation run even when schema is omitted.

editCollectionSchema.schema is optional, and per Line 970 the model is only reassigned when schema !== undefined. But lines 981-997 (getConnection, clearCompiledModel, getCompiledModel, createUniqueIndexes) execute unconditionally, and the collection_updated event still fires at Line 1006 — so a request that omits schema does needless connection/model work and emits a misleading "updated" event for a no-op.

♻️ Suggested guard
-    const connection = await getConnection(projectId);
-    const finalCollectionName = project.resources.db.isExternal
-      ? collectionName
-      : `${project._id}_${collectionName}`;
-
-    clearCompiledModel(connection, finalCollectionName);
-
-    const Model = getCompiledModel(
-      connection,
-      project.collections[collectionIndex],
-      projectId,
-      project.resources.db.isExternal,
-    );
-
-    if (project.collections[collectionIndex].model) {
-      await createUniqueIndexes(Model, project.collections[collectionIndex].model);
-    }
+    if (schema !== undefined) {
+      const connection = await getConnection(projectId);
+      const finalCollectionName = project.resources.db.isExternal
+        ? collectionName
+        : `${project._id}_${collectionName}`;
+
+      clearCompiledModel(connection, finalCollectionName);
+
+      const Model = getCompiledModel(
+        connection,
+        project.collections[collectionIndex],
+        projectId,
+        project.resources.db.isExternal,
+      );
+
+      await createUniqueIndexes(Model, project.collections[collectionIndex].model);
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 979 -
997, Guard the connection lookup, compiled-model clearing/recreation, and
unique-index creation after project.save() with the same schema-present
condition used when reassigning the model in editCollectionSchema. Also emit the
collection_updated event only when schema !== undefined, so requests omitting
schema perform no model work and do not report a misleading update.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 995-997: Update the model synchronization flow around
createUniqueIndexes in the project collection handling to also remove stale
unique_${normalizedKey}_1 indexes that are no longer represented by the current
schema, including indexes created outside this path. Reuse or extend the
existing index-sync mechanism so MongoDB no longer enforces uniqueness after a
field loses unique: true, while preserving creation of current unique indexes.

---

Outside diff comments:
In `@packages/common/src/index.js`:
- Line 95: Replace syncSchemaPayload with editCollectionSchema in both the
validation-module destructuring import and module.exports in the common index
module, so the controller’s editCollectionSchema.parse call receives the
expected schema.

---

Nitpick comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 979-997: Guard the connection lookup, compiled-model
clearing/recreation, and unique-index creation after project.save() with the
same schema-present condition used when reassigning the model in
editCollectionSchema. Also emit the collection_updated event only when schema
!== undefined, so requests omitting schema perform no model work and do not
report a misleading update.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c5b89cad-4024-4007-a4ed-d40df255cd2f

📥 Commits

Reviewing files that changed from the base of the PR and between 15a5865 and b1a181f.

📒 Files selected for processing (11)
  • apps/dashboard-api/src/__tests__/project.controller.editCollection.test.js
  • apps/dashboard-api/src/app.js
  • apps/dashboard-api/src/controllers/project.controller.js
  • apps/dashboard-api/src/routes/projects.js
  • apps/web-dashboard/src/App.jsx
  • apps/web-dashboard/src/pages/Auth.jsx
  • apps/web-dashboard/src/pages/ConfirmationModal.jsx
  • apps/web-dashboard/src/pages/EditCollection.jsx
  • dynamic_connection_sequence_diagram.md
  • packages/common/src/index.js
  • packages/common/src/utils/input.validation.js
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/web-dashboard/src/App.jsx
  • apps/dashboard-api/src/routes/projects.js
  • packages/common/src/utils/input.validation.js
  • apps/dashboard-api/src/app.js
  • apps/web-dashboard/src/pages/Auth.jsx
  • apps/dashboard-api/src/tests/project.controller.editCollection.test.js
  • dynamic_connection_sequence_diagram.md
  • apps/web-dashboard/src/pages/EditCollection.jsx

Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/dashboard-api/src/controllers/project.controller.js (1)

1015-1020: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Catch-all still forwards raw err.message, risking MongoDB detail/PII leakage.

Non-Zod errors (e.g., from project.save(), getConnection, getCompiledModel, createUniqueIndexes) fall through to new AppError(err.status || 400, err.message), surfacing raw driver/Mongoose messages (which can include field values such as duplicate-key data) directly to the client. This is the same class of concern raised previously on this endpoint; the response envelope and early-return paths are now compliant, but this fallback branch still leaks internal error text.

🛡️ Proposed fix
   } catch (err) {
     if (err instanceof z.ZodError) {
       return next(new AppError(400, err.issues?.[0]?.message || "Validation failed"));
     }
-    return next(new AppError(err.status || 400, err.message));
+    if (err instanceof AppError) {
+      return next(err);
+    }
+    console.error("[updateCollection] Unexpected error:", err);
+    return next(new AppError(500, "Failed to update collection"));
   }

As per coding guidelines, "use AppError for errors, never raw throw, and never expose MongoDB errors to clients."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 1015 -
1020, Update the non-Zod fallback in the controller’s catch block to stop
passing raw err.message into AppError. Preserve the status when available, but
replace the client-facing message with a generic safe error message so errors
from project.save, getConnection, getCompiledModel, and createUniqueIndexes
cannot expose MongoDB or Mongoose details.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/common/src/utils/injectModel.js`:
- Around line 222-232: Move the stale-index removal loop around existingIndexes
so it runs only after duplicate validation and all index creation requirements
succeed. Ensure validation and createIndex failures leave existing unique_*
indexes untouched, while preserving the current drop criteria and createdIndexes
tracking.
- Around line 226-230: Update the dropIndex error handling in the index-refresh
flow around Model.collection.dropIndex so it ignores only errors verified to
mean the index is already absent. Re-throw permission, connectivity,
duplicate-index, and any other unexpected errors to ensure the unique-index
refresh fails instead of continuing.
- Around line 212-225: Update the unique-index reconciliation around
schemaUniqueKeys and existingIndexes to track each field’s desired
required/partial options, not only its index name. Compare existing unique-index
options with those desired options and drop incompatible indexes before
duplicate validation and createIndex. Ensure index-creation failures are caught
and propagated or reported as failures rather than allowing the operation to
report success.

---

Outside diff comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 1015-1020: Update the non-Zod fallback in the controller’s catch
block to stop passing raw err.message into AppError. Preserve the status when
available, but replace the client-facing message with a generic safe error
message so errors from project.save, getConnection, getCompiledModel, and
createUniqueIndexes cannot expose MongoDB or Mongoose details.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 596e159d-6bd5-455c-97bf-d2e7e15f05f1

📥 Commits

Reviewing files that changed from the base of the PR and between b1a181f and a77cdce.

📒 Files selected for processing (2)
  • apps/dashboard-api/src/controllers/project.controller.js
  • packages/common/src/utils/injectModel.js

Comment thread packages/common/src/utils/injectModel.js Outdated
Comment thread packages/common/src/utils/injectModel.js Outdated
Comment thread packages/common/src/utils/injectModel.js Outdated
@yash-pouranik
yash-pouranik temporarily deployed to feature/edit-collection - urBackend-frankfrut PR #364 July 24, 2026 19:55 — with Render Destroyed
@vercel
vercel Bot temporarily deployed to Preview – urbackend July 24, 2026 19:55 Inactive
@yash-pouranik
yash-pouranik merged commit e506280 into main Jul 24, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant