Skip to content

feat: Api Schema#30

Merged
hsc00 merged 13 commits intomainfrom
feat/api-schema
Mar 27, 2026
Merged

feat: Api Schema#30
hsc00 merged 13 commits intomainfrom
feat/api-schema

Conversation

@hsc00
Copy link
Copy Markdown
Owner

@hsc00 hsc00 commented Mar 27, 2026

Summary

This PR defines and formalizes the consumer-facing GraphQL API contract for the DAM upload flow (US-01: Agree API schema), adds domain-first artifacts and tests to support that contract, and provides ADRs and docs that record the agreed surface and architectural guidance. The change moves the epic’s US-01 from a bootstrap/schema-and-scaffold implementation to an API-contract-first story.

Changes Overview

API Contract & Schema

  • Added GraphQL SDL (src/GraphQL/Schema/schema.graphql) defining:
    • Query: asset(id: ID!): Asset (authenticated, account-scoped)
    • Mutations: startUpload(input: StartUploadInput!): StartUploadPayload! and completeUpload(input: CompleteUploadInput!): CompleteUploadPayload!
    • Scalars: DateTime, ByteCount
    • Enums: AssetStatus (PENDING/UPLOADED/FAILED), UploadHttpMethod, UploadCompletionProofSource
    • Types for Asset, UploadTarget, completion proof descriptor, parameters, payload wrappers, and UserError
  • Clients must check payload.success != null before reading success fields (contract semantics documented).

Domain Layer

  • New domain entity: App\Domain\Asset\Asset
    • Factory createPending(UploadId, AccountId) (generates UUIDv4 asset id)
    • State transitions: markUploaded(...) and markFailed() with invariant checks throwing AssetDomainException
    • Getters and equals() comparing by asset id
  • New value objects:
    • AssetId (UUID-validated, strict version/variant enforcement)
    • UploadId (UUID v1–v5 compatible validation)
    • AccountId (trim + non-empty validation)
  • New enum: App\Domain\Asset\AssetStatus (PENDING, UPLOADED, FAILED)
  • New repository contract: App\Domain\Asset\AssetRepositoryInterface (save(Asset), findById(AssetId))
  • New domain exception: App\Domain\Asset\Exception\AssetDomainException

Documentation & Architecture

  • ADR-03 (docs/adr/03-domain-first-idempotent-upload.md): Domain-first presigned-upload architecture, idempotency via uploadId + DB UNIQUE(upload_id), persistence conflict handling, and adapter responsibilities for presigned URL generation.
  • API consumer doc (docs/api/01-agree-api-schema.md): Authoritative consumer-facing contract and semantics (detailed explanations, types, and error handling).
  • Implementation log (docs/logs/01-agree-api-schema.md): Notes that GraphQL SDL is authoritative and lists files in scope.
  • Epic update (.github/tasks/epics/epic-01-bootstrap/EPIC-01.md): Replaced previous US-01 (bootstrap scaffold) with US-01: Agree API schema; the original bootstrap document was removed.

Tests

  • Unit tests added:
    • tests/Unit/Domain/Asset/AssetTest.php — verifies lifecycle, metadata, timestamps, exceptions, and equals()
    • tests/Unit/Domain/Asset/ValueObject/AccountIdTest.php — trimming, string casting, invalid input behavior
    • tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php — UUID validation, string casting, invalid inputs

Config, CI & Security

  • composer.json: added ramsey/uuid "^4.9" dependency
  • .gitignore: removed catch-all *.cache entry (retains scoped cache ignores)
  • .gitleaks.toml: added explicit rule ids (aws-access-key, private-key) and broadened private-key regex
  • .github/workflows/security.yml: Semgrep & Gitleaks checkout changed to fetch-depth: 0 (full clone)
  • Removed artifact: .phpunit.cache/test-results file was deleted (cache removed)

Architecture Alignment

  • Follows domain-driven, clean-architecture principles: Domain entities, value objects, enums and interfaces are implementation-agnostic; GraphQL acts as an adapter; ADR documents domain-first idempotency approach.
  • Uses PHP 8 features (strict types, readonly, enums) and defensive domain invariants.
  • Interface-first approach leaves persistence/adapters to subsequent stories (US-02/US-03).

Testing & Risk

  • Tests cover core domain behavior and validation; domain exceptions enforce correct state transitions.
  • Low-to-medium risk: documentation-only items are low risk; domain lifecycle logic and UUID handling are medium risk but covered by tests. Repository-interface implementations and persistence behavior remain to be implemented in follow-up work.

Deliverables / Acceptance

  • API contract defined and documented (GraphQL SDL + consumer docs)
  • Domain model and repository contract provided to support implementation
  • ADR and implementation log recorded
  • Unit tests added for domain artifacts
  • Epic updated to reflect the agreed API-schema scope

@hsc00 hsc00 linked an issue Mar 27, 2026 that may be closed by this pull request
9 tasks
@hsc00 hsc00 added the enhancement New feature or request label Mar 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

📝 Walkthrough

Walkthrough

Adds a domain model for assets (value objects, Asset aggregate, status enum, repository interface), a GraphQL upload API contract, ADR documenting idempotent presigned-upload flow, tests for domain/value objects, and several CI/config updates and dependency additions.

Changes

Cohort / File(s) Summary
Epic & Tasks
\.github/tasks/epics/epic-01-bootstrap/EPIC-01.md, \.github/tasks/epics/epic-01-bootstrap/US-01-agree-api-schema.md, \.github/tasks/epics/epic-01-bootstrap/US-01-bootstrap-schema-domain-scaffold.md
Replaced the previous US-01 bootstrap task with a new US-01 "Agree API schema" story; added the new user-story doc and removed the old scaffold doc.
Architecture & API Docs
docs/adr/03-domain-first-idempotent-upload.md, docs/api/01-agree-api-schema.md, docs/logs/01-agree-api-schema.md
Added ADR describing domain-first idempotent presign/upload flow; added finalized GraphQL contract documentation and an implementation log for the agreed schema.
Domain Aggregate & Behavior
src/Domain/Asset/Asset.php, src/Domain/Asset/AssetStatus.php, src/Domain/Asset/Exception/AssetDomainException.php
Introduced Asset aggregate with factory createPending, state transitions markUploaded/markFailed, state-validation throwing AssetDomainException, and new AssetStatus enum.
Value Objects
src/Domain/Asset/ValueObject/AccountId.php, src/Domain/Asset/ValueObject/UploadId.php, src/Domain/Asset/ValueObject/AssetId.php
Added validated, immutable value objects: AccountId (non-empty trimmed string), UploadId (UUID v1–v5 pattern validation), AssetId (strict UUID with version/variant validation).
Repository Contract
src/Domain/Asset/AssetRepositoryInterface.php
Added repository interface with save(Asset) and findById(AssetId): ?Asset for persistence abstraction.
GraphQL Schema
src/GraphQL/Schema/schema.graphql
Added GraphQL SDL for upload flow: scalars, enums, Asset and UploadTarget types, Start/Complete mutations and payloads, and related input/output shapes.
Unit Tests
tests/Unit/Domain/Asset/AssetTest.php, tests/Unit/Domain/Asset/ValueObject/AccountIdTest.php, tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php
Added tests covering Asset lifecycle, state-transition errors, equals(), and value-object validation/formatting behaviors.
Configuration & CI
composer.json, \.gitignore, \.github/workflows/security.yml, \.gitleaks.toml, \.phpunit.cache/test-results
Added ramsey/uuid dependency; removed catch-all *.cache ignore; security workflow changed to full checkout (fetch-depth: 0); gitleaks rules given explicit ids and private-key regex broadened; removed cached PHPUnit test-results file.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant GraphQL as "GraphQL API"
    participant App as "Application Layer"
    participant Domain as "Domain (Asset Aggregate)"
    participant Repo as "Repository / DB"
    participant Storage as "Storage Adapter"

    Client->>GraphQL: startUpload(input with uploadId, checksum, metadata)
    GraphQL->>App: startUpload(input)
    App->>Domain: find by UploadId?
    Domain->>Repo: findByUploadId(uploadId)
    Repo-->>Domain: existing Asset|null
    alt not found
        Domain->>Domain: createPending(uploadId, accountId)
        Domain->>Repo: save(new Asset) 
        Repo-->>Domain: saved (may raise unique-key error)
        Note right of Repo: On UNIQUE(upload_id) conflict,\nRepo re-queries by uploadId and returns existing
    end
    App->>Storage: request presigned uploadTarget (for uploadId/asset)
    Storage-->>App: uploadTarget (url, method, headers, completionProofDescriptor)
    App-->>GraphQL: StartUploadPayload (success with asset, uploadTarget, uploadGrant)
    GraphQL-->>Client: response

    Client->>Storage: perform PUT/POST to uploadTarget
    Client->>GraphQL: completeUpload(assetId, uploadGrant, completionProof)
    GraphQL->>App: completeUpload(...)
    App->>Repo: findById(assetId)
    Repo-->>App: Asset|null
    App->>Domain: validate completionProof & markUploaded()
    Domain->>Repo: save(updated Asset status UPLOADED)
    Repo-->>Domain: saved
    App-->>GraphQL: CompleteUploadPayload (success.asset)
    GraphQL-->>Client: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble at schemas, neat and small,
New IDs hop in, standing tall.
Presigned paths and statuses sing,
A domain-first carrot for our spring.
Hooray — uploads now have a home! 🎉

🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author; the required template sections (Summary, Changes, Testing, Reviewer Notes) are entirely missing. Add a complete pull request description following the repository template, including a summary of changes, affected files/components, testing status, and relevant reviewer notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 16.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat: Api Schema' is vague and generic, using non-descriptive terminology that doesn't convey the specific nature of this substantial changeset. Consider a more specific title that captures the main change, such as 'feat: Define DAM upload API schema and domain asset modeling' or 'feat: Establish GraphQL schema and asset domain entities'.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/api-schema

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
Copy Markdown

@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: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitignore:
- Around line 59-60: Remove the duplicate PHPUnit ignore lines so only one
"PHPUnit" section remains (consolidate the repeated entries into the existing
PHPUnit block) and keep the .php-cs-fixer.cache entry as-is; also update the
PR/commit summary to remove the incorrect claim about replacing a broad
"*.cache" pattern and instead state that this change simply adds
.php-cs-fixer.cache following the existing tool-specific cache pattern.

In `@src/Application/Asset/CompleteUploadService.php`:
- Around line 16-24: The complete(string $assetId): Asset method in
CompleteUploadService currently marks assets uploaded with placeholder metadata
without verifying credentials; change complete to accept and validate the upload
grant/completion proof (e.g., add parameters like $uploadGrant or
$completionProof), fetch and verify those credentials before proceeding (use
repository->findById to load the Asset, then validate the provided proof against
whatever verification logic or service exists), and only call
Asset::markUploaded with the actual MIME type, filename, and size obtained from
the validated proof or upload metadata; if validation fails or metadata is
missing, throw an appropriate exception and do not call
repository->save($asset).

In `@src/Application/Asset/StartUploadService.php`:
- Around line 26-30: The method that returns the payload (the array containing
'asset', 'uploadTarget', 'uploadGrant' in StartUploadService) is returning
uploadTarget as null which violates the GraphQL type
StartUploadSuccess.uploadTarget: UploadTarget!; update the return value so
'uploadTarget' is a non-null UploadTarget instance/array matching the
UploadTarget shape expected by your schema (or, if no target is available,
throw/return an explicit error variant instead of null); locate the method in
StartUploadService that returns ['asset'=> $asset, 'uploadTarget'=> null,
'uploadGrant'=> ''] and replace the null with a properly constructed
UploadTarget object/associative array (or adjust control flow to return an
error) so the resolver always returns a non-null UploadTarget.
- Around line 21-25: The start method in StartUploadService is not idempotent:
it always calls Asset::createPending and saves, causing duplicates on retries;
change start(string $uploadId, string $accountId) to first lookup an existing
asset by uploadId via the repository (e.g., repository->findByUploadId or
similar), return the existing pending/started asset if found, and only call
Asset::createPending + repository->save when no asset exists; also make the save
conflict-safe by re-querying after a failed unique constraint or handling a
concurrent-insert exception to return the existing asset instead of creating a
duplicate.

In `@src/Domain/Asset/Asset.php`:
- Around line 62-72: The markUploaded method currently allows transitions from
any non-UPLOADED state and accepts negative sizes; update it to only allow the
upload transition when the current status is exactly AssetStatus::PENDING and
the provided $size is >= 0, otherwise throw an AssetDomainException (use a clear
message like "Invalid asset state or size for upload"); keep setting $filename,
$contentType, $size, $status = AssetStatus::UPLOADED and $updatedAt = new
DateTimeImmutable() only after those guards pass; reference the method name
markUploaded, the enum constants AssetStatus::PENDING and AssetStatus::UPLOADED,
and the exception class AssetDomainException so reviewers can locate the change.

In `@src/Domain/Asset/AssetRepositoryInterface.php`:
- Line 11: The repository interface method signature public function
findById(string $id): ?Asset; uses a vague parameter name; rename the parameter
to $assetId in AssetRepositoryInterface (method findById) and update all
implementing classes and call sites to accept and pass $assetId so the contract
and implementations stay consistent and intent is clearer.

In `@src/Domain/Asset/ValueObject/UploadId.php`:
- Around line 9-17: The constructor of UploadId currently uses a hand-rolled
UUID regex (UUID_PATTERN) and preg_match to validate $value; replace that with
Ramsey\Uuid\Uuid::isValid($value) in the UploadId::__construct, remove the
UUID_PATTERN constant and the preg_match check, and keep throwing
\InvalidArgumentException('Invalid UploadId format') when Uuid::isValid returns
false; ensure the Ramsey\Uuid\Uuid class is referenced (import or
fully-qualified) so validation uses the library API.

In `@src/GraphQL/Schema/schema.graphql`:
- Around line 84-94: Update the GraphQL field descriptions to document browser
CORS requirements: for the upload contract described by fields url, method
(UploadHttpMethod), signedHeaders ([UploadParameter!]) and completionProof
(UploadCompletionProofDescriptor!), state that if url is cross-origin the upload
origin must allow all header names listed in signedHeaders via
Access-Control-Allow-Headers during preflight and must expose the header name
returned for completionProof via Access-Control-Expose-Headers (and allow the
requesting origin in Access-Control-Allow-Origin / allow credentials if used) so
a browser client can send the signed headers and read the completionProof
response header to finish the flow.
- Around line 15-17: The schema's ByteCount scalar lacks a canonical wire
format, risking loss for values > 2^53-1; update the schema comment for scalar
ByteCount to state that fileSizeBytes MUST be sent as a JSON string (e.g.,
"12345678901234567890") and then make the scalar implementation (the parseValue,
parseLiteral, and serialize functions for the ByteCount scalar) accept and emit
strings only (or accept numeric input but convert to string for downstream),
ensuring consistency between schema docs and the ByteCount
resolvers/serializers; refer to the ByteCount scalar declaration and any field
named fileSizeBytes to locate where to update comments and the scalar's
parser/serializer code.

In `@tests/Unit/Domain/Asset/AssetTest.php`:
- Around line 37-40: The test must ensure the Asset id is not the same as the
UploadId: in AssetTest (the createPending() test) add an assertion comparing
$asset->getId() to $uploadId (e.g. assertNotSame or assertNotEquals) to
guarantee the Asset id produced by createPending() is distinct from the provided
$uploadId; reference the existing $asset->getId(), $uploadId and the
createPending() fixture when adding this assertion.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d7390a16-b77d-4676-b8de-d26d83126ada

📥 Commits

Reviewing files that changed from the base of the PR and between 968e56c and 29ae9fc.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • .github/tasks/epics/epic-01-bootstrap/EPIC-01.md
  • .github/tasks/epics/epic-01-bootstrap/US-01-agree-api-schema.md
  • .github/tasks/epics/epic-01-bootstrap/US-01-bootstrap-schema-domain-scaffold.md
  • .gitignore
  • composer.json
  • docs/adr/03-domain-first-idempotent-upload.md
  • docs/api/01-agree-api-schema.md
  • docs/logs/01-agree-api-schema.md
  • src/Application/Asset/CompleteUploadService.php
  • src/Application/Asset/StartUploadService.php
  • src/Domain/Asset/Asset.php
  • src/Domain/Asset/AssetRepositoryInterface.php
  • src/Domain/Asset/AssetStatus.php
  • src/Domain/Asset/Exception/AssetDomainException.php
  • src/Domain/Asset/ValueObject/AccountId.php
  • src/Domain/Asset/ValueObject/UploadId.php
  • src/GraphQL/Schema/schema.graphql
  • tests/Unit/Application/Asset/CompleteUploadServiceTest.php
  • tests/Unit/Domain/Asset/AssetTest.php
  • tests/Unit/Domain/Asset/ValueObject/AccountIdTest.php
  • tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php
💤 Files with no reviewable changes (1)
  • .github/tasks/epics/epic-01-bootstrap/US-01-bootstrap-schema-domain-scaffold.md

Comment thread .gitignore Outdated
Comment thread src/Application/Asset/CompleteUploadService.php Outdated
Comment thread src/Application/Asset/StartUploadService.php Outdated
Comment thread src/Application/Asset/StartUploadService.php Outdated
Comment thread src/Domain/Asset/Asset.php
Comment thread src/Domain/Asset/AssetRepositoryInterface.php Outdated
Comment thread src/Domain/Asset/ValueObject/UploadId.php Outdated
Comment thread src/GraphQL/Schema/schema.graphql
Comment thread src/GraphQL/Schema/schema.graphql
Comment thread tests/Unit/Domain/Asset/AssetTest.php
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/security.yml:
- Around line 18-19: Change the GitHub Actions checkout depth from the
unnecessary full history setting to a shallow clone by replacing the checkout
step's fetch-depth value "fetch-depth: 0" with "fetch-depth: 1"; update the
workflow entry that currently contains the literal "fetch-depth: 0" so Semgrep
p/ci still runs correctly but the checkout is faster and uses fewer resources.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a0c9601-1275-4cb4-bba8-e076859b7e1d

📥 Commits

Reviewing files that changed from the base of the PR and between 29ae9fc and d8ace1e.

📒 Files selected for processing (2)
  • .github/workflows/security.yml
  • .gitleaks.toml

Comment thread .github/workflows/security.yml
Copy link
Copy Markdown

@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: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.phpunit.cache/test-results:
- Line 1: The `.phpunit.cache/test-results` cache file is tracked in git despite
`.gitignore` listing `.phpunit.cache/`; remove it from version control by
untracking that specific file (use git rm --cached on
.phpunit.cache/test-results), commit the removal, and push the change so the
generated cache stays ignored going forward; verify the `.gitignore` contains
the `.phpunit.cache/` pattern and that no other cache files are still tracked.

In `@docs/adr/03-domain-first-idempotent-upload.md`:
- Line 29: The ADR says uploadId must be UUID v4 but the validation in
UploadId.php currently allows versions 1-8 via the regex with [1-8]; decide
which is correct and update accordingly: if policy is v4-only, change the regex
in UploadId.php (the upload id validation logic / UploadId class or function
that builds the UUID pattern) to only accept version 4 (replace the version char
class with 4), or if broader versions are acceptable, update
docs/adr/03-domain-first-idempotent-upload.md to state that UUID v1–v8 are
allowed; ensure the change targets the UploadId validation symbol and the ADR
text so both match.

In `@src/Domain/Asset/Asset.php`:
- Around line 96-104: The markFailed() method currently updates updatedAt even
when status is already AssetStatus::FAILED; change markFailed() to be idempotent
by returning early if $this->status === AssetStatus::FAILED (so it neither
throws nor updates updatedAt), keeping the existing guard that throws when
$this->status === AssetStatus::UPLOADED; update or add a unit test to assert
that calling Asset::markFailed() twice leaves status as AssetStatus::FAILED and
does not change updatedAt, referencing the markFailed() method,
AssetStatus::FAILED, AssetStatus::UPLOADED, updatedAt, and AssetDomainException.
- Around line 41-57: Replace the manual UUID generation in Asset::generateUuidV4
with ramsey/uuid: remove the random_bytes/bit-mangling and bin2hex logic in
generateUuidV4 and instead return \Ramsey\Uuid\Uuid::uuid4()->toString(), adding
the appropriate use/import for Ramsey\Uuid\Uuid at the top of the Asset class
file; this leverages the existing dependency and eliminates the custom
implementation.

In `@src/Domain/Asset/ValueObject/AssetId.php`:
- Around line 7-29: AssetId duplicates UUID validation logic found in UploadId
via the same UUID_PATTERN and manual preg_match; extract that shared logic by
creating a UuidValidation trait (with the UUID_PATTERN constant and a protected
static assertValidUuid(string $value, string $errorMessage): void) or use
Ramsey\Uuid\Uuid::isValid(), then update AssetId::__construct to call the shared
assertValidUuid (or Uuid::isValid) instead of repeating the regex; while doing
this, align the property declaration style with UploadId (use constructor
promotion or match its style) and ensure AssetId still exposes the $value and
__toString() unchanged.

In `@tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php`:
- Around line 67-70: The private test helper createUploadId() is an unnecessary
indirection for instantiating UploadId; replace calls to createUploadId($value)
with direct constructions new UploadId($value) and remove the private function
createUploadId() from the test class to simplify the tests and reduce cognitive
overhead. Ensure all usages in the test file are updated to use new
UploadId(...) and run the unit tests to confirm no references remain to
createUploadId().
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 86f8dd6d-638c-46a3-88c2-14e2a03125ba

📥 Commits

Reviewing files that changed from the base of the PR and between d8ace1e and 6adbc23.

📒 Files selected for processing (13)
  • .gitignore
  • .gitleaks.toml
  • .phpunit.cache/test-results
  • docs/adr/03-domain-first-idempotent-upload.md
  • docs/api/01-agree-api-schema.md
  • src/Domain/Asset/Asset.php
  • src/Domain/Asset/AssetRepositoryInterface.php
  • src/Domain/Asset/Exception/AssetDomainException.php
  • src/Domain/Asset/ValueObject/AssetId.php
  • src/Domain/Asset/ValueObject/UploadId.php
  • src/GraphQL/Schema/schema.graphql
  • tests/Unit/Domain/Asset/AssetTest.php
  • tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php
💤 Files with no reviewable changes (1)
  • .gitignore

Comment thread .phpunit.cache/test-results Outdated
Comment thread docs/adr/03-domain-first-idempotent-upload.md
Comment thread src/Domain/Asset/Asset.php
Comment thread src/Domain/Asset/Asset.php
Comment thread src/Domain/Asset/ValueObject/AssetId.php
Comment thread tests/Unit/Domain/Asset/ValueObject/UploadIdTest.php
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
src/Domain/Asset/ValueObject/AssetId.php (1)

9-20: 🧹 Nitpick | 🔵 Trivial

Extract UUID validation to shared code path.

This repeats UUID regex/guard logic that already exists in another value object, which increases maintenance risk if rules change. Centralize the assertion in a shared helper/trait and call it here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Domain/Asset/ValueObject/AssetId.php` around lines 9 - 20, The UUID
validation in AssetId (private const UUID_PATTERN and the preg_match check in
__construct) should be moved to a shared assertion (e.g., a
UuidAssertion::assertValid(string $value) static method or a
ValueObjectUuidTrait::assertUuid(string $value)) and invoked from
AssetId::__construct; remove the duplicate UUID_PATTERN constant and preg_match
from AssetId, import/use the shared helper, and have AssetId throw the same
\InvalidArgumentException when the shared assertion fails so behavior remains
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Domain/Asset/ValueObject/AssetId.php`:
- Around line 9-20: The UUID validation in AssetId (private const UUID_PATTERN
and the preg_match check in __construct) should be moved to a shared assertion
(e.g., a UuidAssertion::assertValid(string $value) static method or a
ValueObjectUuidTrait::assertUuid(string $value)) and invoked from
AssetId::__construct; remove the duplicate UUID_PATTERN constant and preg_match
from AssetId, import/use the shared helper, and have AssetId throw the same
\InvalidArgumentException when the shared assertion fails so behavior remains
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 397bf1cc-18e6-45e4-a365-38eac8ced29a

📥 Commits

Reviewing files that changed from the base of the PR and between 6adbc23 and 93cee65.

📒 Files selected for processing (3)
  • .phpunit.cache/test-results
  • src/Domain/Asset/ValueObject/AssetId.php
  • src/Domain/Asset/ValueObject/UploadId.php
💤 Files with no reviewable changes (1)
  • .phpunit.cache/test-results

@hsc00 hsc00 merged commit 80a4a73 into main Mar 27, 2026
7 checks passed
@hsc00 hsc00 deleted the feat/api-schema branch March 27, 2026 13:58
This was referenced Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

US-01: Agree API Schema

1 participant