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

57 lines
3.8 KiB
Markdown

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