Ubisam-MobileGit/tasks/004-gitea-api-client.md
choiminyeong fdda16442a Review and freeze TASK-004/005/006; fix 409/422 typed failures
Independent read-only review of 8a47d30/bbbff77/16c46a5 per
handoff/claude-master-handoff.md's review-and-freeze step. Verified
build/test/lint independently rather than trusting recorded evidence.

Fixed P2: requireSuccess() collapsed HTTP 409/422 into the generic
InvalidResponse, which would have blocked TASK-007's planned
conflict/unprocessable error states. Added ApiFailure.Conflict and
ApiFailure.Unprocessable with test coverage.

Recorded a P3 (CPU-bound work on the Main dispatcher in
CodeBrowserViewModel) for TASK-016 rather than fixing it now. Closed
TASK-005/006's mutual contract-freeze checkboxes. No P0/P1 found.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 12:54:46 +09:00

3.8 KiB

TASK-004: Implement the read-only Gitea API client

Status: implemented and independently reviewed by Claude; one P2 fixed (409/422 typed failures) Owner: Codex Reviewer: Claude

Writable paths

  • app/src/main/java/com/ubisam/ubgitea/core/network/
  • app/src/main/java/com/ubisam/ubgitea/data/
  • app/src/main/java/com/ubisam/ubgitea/domain/
  • app/src/test/java/com/ubisam/ubgitea/core/network/
  • app/src/test/java/com/ubisam/ubgitea/data/
  • app/src/test/java/com/ubisam/ubgitea/domain/
  • app/build.gradle.kts
  • gradle/libs.versions.toml
  • docs/decisions/003-http-client.md
  • tasks/004-gitea-api-client.md

Outcome

Connect the repository browsing domain to authenticated, paginated Gitea API calls while keeping DTOs and tokens out of domain and UI layers.

Acceptance criteria

  • Base URL is configurable with the approved production default.
  • DTOs are separate from domain models and mapping is tested.
  • Repository, branch, tag, tree, file, and commit endpoints support pagination where provided.
  • Authentication is injected without logging or exposing tokens.
  • HTTP, authorization, rate-limit, parsing, cancellation, and offline failures map to typed failures.
  • Branch, ref, owner, repository, and path values are safely encoded as individual URL components.
  • Tests use in-memory transport fixtures and a local MockWebServer and never contact production.

Required checks

./gradlew assembleDebug
./gradlew testDebugUnitTest
./gradlew lintDebug

Evidence

  • Implemented GiteaRepositoryBrowserApi for repositories, branches, tags, directory contents, Base64 files, and commits against Gitea 1.23-compatible response contracts.
  • Added immutable TagSummary and tag-list domain/use-case contract.
  • Focused adapter tests cover mapping, pagination, component encoding, authenticated headers, strict file decoding, malformed responses, 404, 429, and local HTTP transport behavior.
  • Full debug build, unit tests, lint, diff check, and sensitive-pattern scan are recorded in docs/development-log.md.

Claude independent review closure

Reviewed read-only at commit 8a47d30 (audited together with TASK-005 bbbff77 and TASK-006 16c46a5 per handoff/claude-master-handoff.md's review-and-freeze instruction). Independently reran assembleDebug testDebugUnitTest lintDebug --rerun-tasks at 16c46a5: BUILD SUCCESSFUL, 102 unit tests across 23 suites, 0 failures/errors, 0 lint errors; git diff --check clean; sensitive-pattern scan clean (one match was the test itself asserting client_secret is absent from the authorization URI).

  • P2 (fixed): HttpTransport.kt:94-102 requireSuccess() mapped only 401/403/404/429/5xx to typed ApiFailure; HTTP 409 and 422 fell through to the generic InvalidResponse, losing the distinction the review checklist and the upcoming TASK-007 UDF error states (conflict/unprocessable) require. Fixed by adding ApiFailure.Conflict (409) and ApiFailure.Unprocessable (422) and extending GiteaRepositoryBrowserApiTest's status-mapping test to cover both. Closure: full gates rerun and passing (above); TASK-007 can now type its conflict/unprocessable states directly from core/network.
  • URL encoding (verified, no finding): owner/name/path segments use HttpUrl.Builder.addPathSegment individually (repoUrl, RepositoryPath.segments()); ref and path-filter values use addQueryParameter. Confirmed by GiteaRepositoryBrowserApiTest's encoding assertions on repository names containing spaces/+/# and refs containing +//.
  • Pagination/page overflow (verified, no finding): listTree's local pagination uses Long arithmetic before coercing to Int, so no overflow on large page values; Int.MAX_VALUE page numbers are rejected via safeNextPage() instead of wrapping.
  • No P0/P1 found. No other P2/P3 raised against this task's scope.