Skip to content

feat: filter _design docs from all replication#215

Merged
sleidig merged 3 commits intomasterfrom
fix/skip-design-doc-replication
Feb 24, 2026
Merged

feat: filter _design docs from all replication#215
sleidig merged 3 commits intomasterfrom
fix/skip-design-doc-replication

Conversation

@sleidig
Copy link
Member

@sleidig sleidig commented Feb 24, 2026

because we manage them client-side

Summary by CodeRabbit

  • New Features

    • Replication now skips documents whose IDs match configurable ignored prefixes (defaults to _design/), preventing them from entering replication flows.
  • Tests

    • Added comprehensive tests validating prefix-based filtering and permission behavior across replication endpoints and change streams.
  • Documentation

    • Updated environment template documenting REPLICATION_IGNORED_PREFIXES and its default/override semantics.

because we manage them client-side
Copilot AI review requested due to automatic review settings February 24, 2026 11:13
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab1e2ed and cdc8c08.

📒 Files selected for processing (1)
  • src/restricted-endpoints/replication/document-filter/document-filter.service.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/restricted-endpoints/replication/document-filter/document-filter.service.ts

📝 Walkthrough

Walkthrough

Introduces DocumentFilterService to exclude documents from replication by ID prefixes (default "_design/"), reads REPLICATION_IGNORED_PREFIXES from config, and integrates this filter into ChangesController and BulkDocumentService to skip non‑replicable IDs before permission checks.

Changes

Cohort / File(s) Summary
Document Filter Service
src/restricted-endpoints/replication/document-filter/document-filter.service.ts, src/restricted-endpoints/replication/document-filter/document-filter.service.spec.ts
Adds new service that parses REPLICATION_IGNORED_PREFIXES (defaults to ["_design/"], empty string disables) and exposes isReplicable(docId); includes unit tests for default and custom prefix behavior.
Changes Controller Integration
src/restricted-endpoints/replication/changes/changes.controller.ts, src/restricted-endpoints/replication/changes/changes.controller.spec.ts
Injects DocumentFilterService and skips non-replicable change IDs (updates lastProcessedSeq) before permission checks; adds test ensuring _design/ changes are omitted.
Bulk Document Service Integration
src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts, src/restricted-endpoints/replication/bulk-document/bulk-document.service.spec.ts
Injects DocumentFilterService; filters replicable IDs in filterBulkGetResponse, filterAllDocsResponse, filterBulkDocsRequest, and filterFindResponse prior to existing permission logic; adds tests for design doc filtering across response types.
Module & Configuration
src/restricted-endpoints/replication/replication.module.ts, .env.template
Registers DocumentFilterService in ReplicationModule providers and documents REPLICATION_IGNORED_PREFIXES in .env.template (commented lines).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChangesController
  participant DocumentFilterService
  participant PermissionService
  participant CouchDB

  Client->>ChangesController: request changes stream
  ChangesController->>DocumentFilterService: isReplicable(change.id)?
  alt not replicable
    DocumentFilterService-->>ChangesController: false
    ChangesController-->>Client: skip change (advance seq)
  else replicable
    DocumentFilterService-->>ChangesController: true
    ChangesController->>PermissionService: check read permission(change.id)
    alt permitted
      PermissionService-->>ChangesController: allowed
      ChangesController-->>Client: emit change
    else denied
      PermissionService-->>ChangesController: denied
      ChangesController-->>Client: record lostPermission / skip
    end
    ChangesController->>CouchDB: fetch docs/metadata (if needed)
    CouchDB-->>ChangesController: docs/metadata
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hop through IDs and give them a test,
"_design/" I skip so replication can rest.
Prefixes tuned from env or left alone,
I guard the stream so only needed docs are shown. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: filtering _design documents from all replication endpoints throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/skip-design-doc-replication

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 and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements filtering of CouchDB design documents from replication endpoints to support client-side management of design documents. A new DocumentFilterService is introduced with configurable prefix-based filtering that defaults to excluding _design/ documents.

Changes:

  • Added DocumentFilterService with environment-configurable prefix filtering for replication
  • Applied document filtering to all replication endpoints: changes, bulk_get, all_docs, bulk_docs, and find
  • Added comprehensive test coverage for design document filtering across all affected endpoints

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/restricted-endpoints/replication/document-filter/document-filter.service.ts New service implementing prefix-based document filtering with REPLICATION_IGNORED_PREFIXES configuration
src/restricted-endpoints/replication/document-filter/document-filter.service.spec.ts Comprehensive tests for DocumentFilterService covering default and custom configurations
src/restricted-endpoints/replication/replication.module.ts Registers DocumentFilterService as a provider in the replication module
src/restricted-endpoints/replication/changes/changes.controller.ts Applies document filtering to the changes feed endpoint
src/restricted-endpoints/replication/changes/changes.controller.spec.ts Adds test verifying design documents are filtered from changes feed
src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts Applies document filtering to bulk_get, all_docs, bulk_docs, and find operations
src/restricted-endpoints/replication/bulk-document/bulk-document.service.spec.ts Adds tests verifying design documents are filtered from all bulk operations
.env.template Documents the REPLICATION_IGNORED_PREFIXES configuration option

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/restricted-endpoints/replication/changes/changes.controller.ts (1)

104-106: lastProcessedSeq is not advanced when a design doc is skipped.

Because continue bypasses the lastProcessedSeq = change.seq assignment at Line 128, in a paginated batch where a design doc sits between the last processed change and an overflow point, the returned last_seq remains behind the design doc's sequence number. The next page request re-includes the design doc from CouchDB, which is immediately skipped again — functionally correct but a needless round-trip for each page boundary that crosses a design doc.

♻️ Advance `lastProcessedSeq` for skipped docs
-      if (!this.documentFilter.isReplicable(change.id)) {
-        continue;
-      }
+      if (!this.documentFilter.isReplicable(change.id)) {
+        lastProcessedSeq = change.seq;
+        continue;
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/restricted-endpoints/replication/changes/changes.controller.ts` around
lines 104 - 106, The loop in changes.controller.ts skips design docs via
documentFilter.isReplicable(change.id) using "continue" but fails to advance
lastProcessedSeq, causing repeated re-fetches; update the loop so
lastProcessedSeq (the variable tracking progress) is set to change.seq for every
change before skipping (i.e., set lastProcessedSeq = change.seq when
documentFilter.isReplicable(change.id) returns false) or move the assignment of
lastProcessedSeq = change.seq outside/above the continue check so it always
advances even for skipped docs (refer to lastProcessedSeq,
documentFilter.isReplicable(change.id), and change.seq).
src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts (1)

86-113: filterBulkDocsRequest: design doc IDs are included in the _all_docs query unnecessarily.

allDocsRequest.keys is built from all request.docs before the isReplicable filter is applied, so design doc IDs are sent to CouchDB even though they'll be discarded in the final docs.filter(). Pre-filtering eliminates the unnecessary lookup.

♻️ Pre-filter replicable docs before building the CouchDB query
 async filterBulkDocsRequest(...): Promise<BulkDocsRequest> {
   const ability = this.permissionService.getAbilityFor(user);
+  const replicableDocs = request.docs.filter((doc) =>
+    this.documentFilter.isReplicable(doc._id),
+  );
   const allDocsRequest: AllDocsRequest = {
-    keys: request.docs.map((doc) => doc._id),
+    keys: replicableDocs.map((doc) => doc._id),
   };
   const response = await firstValueFrom(
     this.couchdbService.post<AllDocsResponse>(db, '_all_docs', allDocsRequest, {
       include_docs: true,
     }),
   );
   return {
     new_edits: request.new_edits,
-    docs: request.docs.filter(
-      (doc) =>
-        this.documentFilter.isReplicable(doc._id) &&
-        this.hasPermissionsForDoc(
-          doc,
-          response.rows.find((responseDoc) => responseDoc.id === doc._id)?.doc,
-          ability,
-        ),
+    docs: replicableDocs.filter((doc) =>
+      this.hasPermissionsForDoc(
+        doc,
+        response.rows.find((responseDoc) => responseDoc.id === doc._id)?.doc,
+        ability,
+      ),
     ),
   };
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts`
around lines 86 - 113, In filterBulkDocsRequest, avoid sending design/doc IDs to
CouchDB by pre-filtering request.docs with this.documentFilter.isReplicable
before building allDocsRequest.keys; create a filteredDocs array (from
request.docs.filter(this.documentFilter.isReplicable)), use filteredDocs.map(doc
=> doc._id) for allDocsRequest.keys when calling this.couchdbService.post, and
then base the returned docs array and permission checks
(this.hasPermissionsForDoc) on filteredDocs and their matching response.rows
entries (matching by id) instead of the original request.docs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.env.template:
- Around line 13-15: Clarify that REPLICATION_IGNORED_PREFIXES replaces the
default rather than extends it: update the .env.template text around the
REPLICATION_IGNORED_PREFIXES entry to state that if the variable is set it
overrides the default "_design/" filter (not appended), that it accepts a
comma-separated list to specify multiple prefixes, and include an example
showing both defaults preserved and additional prefixes (e.g.
REPLICATION_IGNORED_PREFIXES=_design/,_local/) so users know to include
"_design/" when adding others.

---

Nitpick comments:
In `@src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts`:
- Around line 86-113: In filterBulkDocsRequest, avoid sending design/doc IDs to
CouchDB by pre-filtering request.docs with this.documentFilter.isReplicable
before building allDocsRequest.keys; create a filteredDocs array (from
request.docs.filter(this.documentFilter.isReplicable)), use filteredDocs.map(doc
=> doc._id) for allDocsRequest.keys when calling this.couchdbService.post, and
then base the returned docs array and permission checks
(this.hasPermissionsForDoc) on filteredDocs and their matching response.rows
entries (matching by id) instead of the original request.docs.

In `@src/restricted-endpoints/replication/changes/changes.controller.ts`:
- Around line 104-106: The loop in changes.controller.ts skips design docs via
documentFilter.isReplicable(change.id) using "continue" but fails to advance
lastProcessedSeq, causing repeated re-fetches; update the loop so
lastProcessedSeq (the variable tracking progress) is set to change.seq for every
change before skipping (i.e., set lastProcessedSeq = change.seq when
documentFilter.isReplicable(change.id) returns false) or move the assignment of
lastProcessedSeq = change.seq outside/above the continue check so it always
advances even for skipped docs (refer to lastProcessedSeq,
documentFilter.isReplicable(change.id), and change.seq).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5349d7 and b6faf48.

📒 Files selected for processing (8)
  • .env.template
  • src/restricted-endpoints/replication/bulk-document/bulk-document.service.spec.ts
  • src/restricted-endpoints/replication/bulk-document/bulk-document.service.ts
  • src/restricted-endpoints/replication/changes/changes.controller.spec.ts
  • src/restricted-endpoints/replication/changes/changes.controller.ts
  • src/restricted-endpoints/replication/document-filter/document-filter.service.spec.ts
  • src/restricted-endpoints/replication/document-filter/document-filter.service.ts
  • src/restricted-endpoints/replication/replication.module.ts

sleidig and others added 2 commits February 24, 2026 12:36
…r.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@sleidig sleidig merged commit 56f437b into master Feb 24, 2026
4 of 5 checks passed
@sleidig sleidig deleted the fix/skip-design-doc-replication branch February 24, 2026 13:51
@github-project-automation github-project-automation bot moved this from Technical Review to Done in All Tasks & Issues Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants