Skip to content

feat(repository): add the repository and seeder layers - #948

Open
abnegate wants to merge 1 commit into
feat-query-libfrom
feat-repository-seeder
Open

feat(repository): add the repository and seeder layers#948
abnegate wants to merge 1 commit into
feat-query-libfrom
feat-repository-seeder

Conversation

@abnegate

@abnegate abnegate commented Aug 27, 2026

Copy link
Copy Markdown
Member

Split out of #823, which had carried these along with the query-lib migration.

Repository gives a typed collection facade — Repository, Scope, Specification, CompositeSpecification — so a caller composes reusable query fragments instead of assembling Query arrays at each call site. Seeder gives Factory, FactoryDefinition, Fixture and SeederRunner for building test and development data.

Why separately

The migration is a move every caller has to make. These are capabilities we chose. While they shared a branch a reviewer could not take one and leave the other.

Why it targets feat-query-lib

Both are written against the Attribute, Collection and Query value objects #823 introduces, so neither can sit on main until that lands. Retarget once it does.

Chain

Landing order, bottom up:

  1. utopia-php/database#823 — the query-lib migration itself
  2. utopia-php/abuse#124, utopia-php/audit#133, utopia-php/migration#222 — the schema call sites in the libraries
  3. appwrite/appwrite#11649
  4. appwrite-labs/cloud#5410

Stacked on #823 but not part of it, and not required by anything above: #947 (ORM), #948 (repositories and seeding), #949 (migration runner and schema differ).

Every dev-feat-query-lib pin in this train is re-pinned to its branch head whenever one of them moves, so each PR's CI runs against what the others actually contain.

Not verified

Nothing consumes either one — not this library, not appwrite, not cloud. The tests here are unit tests over composition and the factory definitions; neither has been exercised against a real engine.

CI is not being driven to green on this PR. It is parked behind #823 and will be rebased and fixed once that lands.

Splits out of the query-lib migration, which had carried these along with it.

Repository gives a typed collection facade -- Repository, Scope,
Specification and CompositeSpecification -- so a caller composes reusable
query fragments instead of assembling Query arrays at each call site. Seeder
gives Factory, FactoryDefinition, Fixture and SeederRunner for building test
and development data.

Stacked on feat-query-lib rather than main: both are written against the
Attribute, Collection and Query value objects that migration introduces.

Nothing consumes either one yet, in this library or in appwrite or cloud, so
neither has been exercised against a real engine. The tests here are unit
tests over composition and the factory definitions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • 0.69.x

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2bca5e09-605a-459f-9c09-699bb4dacbf5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds repository abstractions for reusable query scopes/specifications and seeder abstractions for factories, fixtures, dependency ordering, and execution.

  • Adds scoped repository reads, specifications, and boolean query composition.
  • Adds Faker-backed document factories and batch creation helpers.
  • Adds fixture loading/cleanup and dependency-aware seeder execution.
  • Adds unit coverage for repository composition, scopes, factories, fixtures, and seeder ordering.

Confidence Score: 3/5

The PR should not merge until global scopes cover ID-based repository operations and fixture cleanup preserves or reports failed deletions.

ID-based repository methods can operate outside configured scopes, and fixture cleanup silently loses tracking for records that remain after deletion errors.

Files Needing Attention: src/Database/Repository/Repository.php, src/Database/Seeder/Fixture.php

Important Files Changed

Filename Overview
src/Database/Repository/Repository.php Adds the repository facade, but ID-based reads and mutations bypass configured global scopes.
src/Database/Repository/CompositeSpecification.php Adds AND/OR specification composition while preserving grouped logical filters.
src/Database/Seeder/Factory.php Adds Faker-backed document factories with overrides and single/batch persistence helpers.
src/Database/Seeder/Fixture.php Adds fixture tracking and cleanup, but failed deletions are forgotten and cannot be retried.
src/Database/Seeder/SeederRunner.php Adds dependency-ordered seeder execution, including concurrent execution of independent ready seeders.

Fix all with Greploop Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
### Issue 1
src/Database/Repository/Repository.php:53-56
**Global scopes are bypassed**

When a repository registers a tenant, soft-delete, or other business scope, `findById()` calls the database directly without applying it; `update()` and `delete()` have the same bypass. An out-of-scope document can therefore be returned, updated, or deleted.

### Issue 2
src/Database/Seeder/Fixture.php:53
**Failed cleanup loses tracking**

When `deleteDocument()` throws during cleanup, the exception is suppressed and the entire tracking list is still cleared. The undeleted record remains in the database while `getCreated()` reports nothing outstanding, preventing cleanup from being retried and contaminating later tests or development data.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(repository): add the repository and..." | Re-trigger Greptile

Comment on lines +53 to +56
public function findById(string $id): Document
{
return $this->db->getDocument($this->collection(), $id);
}

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.

P1 Global scopes are bypassed

When a repository registers a tenant, soft-delete, or other business scope, findById() calls the database directly without applying it; update() and delete() have the same bypass. An out-of-scope document can therefore be returned, updated, or deleted.

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Database/Repository/Repository.php
Line: 53-56

Comment:
**Global scopes are bypassed**

When a repository registers a tenant, soft-delete, or other business scope, `findById()` calls the database directly without applying it; `update()` and `delete()` have the same bypass. An out-of-scope document can therefore be returned, updated, or deleted.

**Knowledge Base Used:**
- [Query construction and execution](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/utopia-php/database/-/docs/query-execution.md)
- [Database orchestration](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/utopia-php/database/-/docs/database-orchestration.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

}
}
}

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.

P1 Failed cleanup loses tracking

When deleteDocument() throws during cleanup, the exception is suppressed and the entire tracking list is still cleared. The undeleted record remains in the database while getCreated() reports nothing outstanding, preventing cleanup from being retried and contaminating later tests or development data.

Knowledge Base Used: Document lifecycle and representation

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/Database/Seeder/Fixture.php
Line: 53

Comment:
**Failed cleanup loses tracking**

When `deleteDocument()` throws during cleanup, the exception is suppressed and the entire tracking list is still cleared. The undeleted record remains in the database while `getCreated()` reports nothing outstanding, preventing cleanup from being retried and contaminating later tests or development data.

**Knowledge Base Used:** [Document lifecycle and representation](https://app.greptile.com/appwrite/-/custom-context/knowledge-base/utopia-php/database/-/docs/document-lifecycle.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

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