Skip to content

feat(usecase): add analyze adapter for pkg/prism#13

Merged
hidetzu merged 1 commit into
mainfrom
feat/usecase-analyze-adapter
Apr 11, 2026
Merged

feat(usecase): add analyze adapter for pkg/prism#13
hidetzu merged 1 commit into
mainfrom
feat/usecase-analyze-adapter

Conversation

@hidetzu
Copy link
Copy Markdown
Owner

@hidetzu hidetzu commented Apr 11, 2026

Summary

PR 7/9 of the Phase 2 series. First of the two PRs that together land `/v1/analyze`. This PR introduces the `internal/usecase` package with a thin adapter over `pkg/prism.Analyze` and adds `github.com/hidetzu/prism v0.3.0` as the first direct dependency. The handler and HTTP routing land in PR 8.

This code is not yet reachable from any HTTP route. It is intentionally dead until PR 8 lands — reviewability was preferred over temporary unused code. Dead-code cleanup is scheduled for v0.2.0 release time.

Design

Why a function field, not an interface

`pkg/prism.Analyze` is a package-level function, not a method on a struct. The conventional approach would be to wrap it in a `Prism` interface and inject that interface, but that adds a wrapper type plus an implementation that just forwards the call — indirection with no benefit. Instead:

type Analyzer struct {
    analyze func(ctx context.Context, opts prism.AnalyzeOptions) (prism.Result, error)
}

func NewAnalyzer() *Analyzer {
    return &Analyzer{analyze: prism.Analyze}
}

Tests inject a fake by writing to the field directly. Production always uses `NewAnalyzer()`. This is an idiomatic Go pattern for adapting package-level functions.

Why `Provider: "github"` is hard-coded

`pkg/prism.AnalyzeOptions.Provider` supports auto-detection when empty, but the Phase 2 instruction §5 states Phase 2 is GitHub-only, and making the provider explicit improves log/debug clarity. If a future phase adds CodeCommit or similar, the adapter fans out per provider (or we flip to auto-detection) at that point.

Why `IncludePatches: false`

This is the `pkg/prism` default and keeps response payloads lightweight. A future API flag can opt in per request if users want diff patches in the response. Phase 2 ships without patches.

Error passthrough

`pkg/prism` exposes four sentinel errors (`ErrInvalidInput`, `ErrUnsupportedProvider`, `ErrAuthRequired`, `ErrUpstreamFailure`) that the HTTP handler in PR 8 will map to `response.Code` via `errors.Is`. The adapter does not rewrap or translate them — they flow through unchanged.

Test plan

7 test cases in `analyze_test.go`:

Test Verifies
`PassesCorrectOptions` All `AnalyzeOptions` fields: `Provider=github`, `PRURL`, `GitHubToken`, `IncludePatches=false`, `Mode=""`, `Language=""`
`EmptyTokenIsPassedThrough` Empty `GitHubToken` is not substituted — Phase 2 supports unauthenticated public-repo calls
`ErrorPassthrough` (×4 sub-tests) All four pkg/prism sentinels flow through `errors.Is`
`ContextPropagated` Context value reaches the underlying call (required for cancellation to work in the middleware timeout layer)
`NewAnalyzer_UsesRealPrism` Constructor smoke check: non-nil Analyzer with populated function field. Does not invoke pkg/prism.

Local checks

  • `go vet ./...` clean
  • `go test ./... -race -count=1` all pass (7 new + all existing)
  • `golangci-lint run` 0 issues
  • `go mod tidy` clean

Dependencies added

```
github.com/hidetzu/prism v0.3.0
```

Per `docs/development_rules.md` §3, this is the first pinned import of the `pkg/prism` library. The version matches the rules doc.

Phase 2 context

# PR Status
1 response codes merged
2 validation helpers merged
3 body_limit middleware merged
4 rate_limit middleware merged
5 concurrency_limit middleware merged
6 wire defensive middleware + E2E test merged (Fly verified)
7 analyze usecase adapter (this PR) open
8 /v1/analyze handler and routing blocked on 7
9 /v1/prompt endpoint blocked on 7

Intended to be squash-merged.

First piece of the /v1/analyze endpoint. Thin adapter between HTTP
handlers and pkg/prism.Analyze, shaped so that handlers will depend
on a small interface defined in the handler package (accept
interfaces, return structs, per docs/development_rules.md §10); the
concrete implementation lives here in internal/usecase.

- AnalyzeInput carries the HTTP-layer-agnostic inputs that a handler
  extracts from its validated AnalyzeRequest.
- Analyzer stores pkg/prism.Analyze in a function field rather than
  wrapping it in an interface. That keeps the indirection minimal
  and still lets tests inject a fake via field assignment without
  maintaining an extra wrapper type.
- NewAnalyzer wires the real pkg/prism.Analyze as the function field.
- Analyze always sets Provider: "github" (Phase 2 is GitHub only per
  the Phase 2 instruction §5) and keeps IncludePatches at the
  pkg/prism default of false. Provider auto-detection is deliberately
  bypassed so the provider is explicit in logs and debug traces.
- pkg/prism's sentinel errors flow through unchanged so the handler
  layer can map them to response.Code via errors.Is.

Tests cover:
  - Happy path with full AnalyzeOptions field inspection (Provider,
    PRURL, GitHubToken, IncludePatches false, Mode/Language empty)
  - Empty GitHubToken passthrough (Phase 2 supports public repos
    without authentication)
  - Error passthrough for all four pkg/prism sentinels (ErrInvalidInput,
    ErrUnsupportedProvider, ErrAuthRequired, ErrUpstreamFailure)
  - Context propagation to the underlying call
  - NewAnalyzer constructor smoke check (non-nil Analyzer with
    non-nil function field)

Adds github.com/hidetzu/prism v0.3.0 as the first direct pkg/prism
dependency. The adapter is not yet wired to an HTTP handler; PR 8
will add /v1/analyze and pull it in.
@hidetzu hidetzu merged commit 71057bc into main Apr 11, 2026
2 checks passed
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