You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,7 +85,7 @@ The Java SDK must also satisfy the canonical, cross-language [SDK Requirements](
85
85
**Deliberately deferred (require the per-resource layer to land first):**
86
86
- §1.2 resource groupings — `client.utilities()`, `client.options()`, and `client.stocks()` are wired today; `client.funds`, `client.markets` still to come.
87
87
- §2 endpoint method coverage; §3 universal parameters; §11 wire-format decoding for the remaining resources (funds, markets). The plumbing (`ParallelArrays.zip` helper for the parallel-arrays shape, `JsonResponseParser`, the `MarketDataResponse<T>` named-response types) is in place — each new endpoint just declares its fields and row builder.
88
-
- §8 request-scoped rate-limit attachment — today the snapshot is client-level (via `client.getRateLimits()`); attaching a per-response snapshot to `Response<T>` is a small follow-up when a consumer needs it.
88
+
-~~§8 request-scoped rate-limit attachment~~**DONE** — every `MarketDataResponse` now exposes `rateLimit()`, parsed from that response's own `x-api-ratelimit-*` headers (request-scoped), alongside the client-level `client.getRateLimits()`.
89
89
- §13 100% coverage threshold via JaCoCo `violationRules`; deferred until the resource layer lands so the threshold meaningfully ratchets functional code, not just scaffolding.
90
90
91
91
When picking up new work, check this list before reaching for the SDK requirements doc — most foundational rules are already encoded in code; missing pieces are deferred deliberately, not by accident.
Copy file name to clipboardExpand all lines: docs/STOCKS_REVIEW_GUIDE.md
+19-10Lines changed: 19 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
This guide walks a reviewer through the `stocks` resource added on the `11_stocks_resource` branch. It is organized by **flow**, not by file.
4
4
5
-
This PR **adopts the conventions the [`options` PR](OPTIONS_REVIEW_GUIDE.md) established** — the `MarketDataResponse<T>` + named-response model, the Builder-based per-endpoint request, nullable fields + `columns` + Option A, and the CSV/HTML facets — and applies them to stocks. The shared layers (transport, retry, rate-limit, response model, `ParallelArrays`, `JsonResponseParser`) are reused **unchanged**; the only shared-layer addition is one tolerant date parser (`MarketDataDates.parseDateOrTimestampField`). If you reviewed the options PR, the load-bearing shape will be familiar — focus your time on §4 (per-endpoint query translation), §5 (the per-endpoint required-column sets + the `news` deserializer) and §7–§8 (batch vs. fan-out, news/earnings specifics).
5
+
This PR **adopts the conventions the [`options` PR](OPTIONS_REVIEW_GUIDE.md) established** — the `MarketDataResponse<T>` + named-response model, the Builder-based per-endpoint request, nullable fields + `columns` + Option A, and the CSV/HTML facets — and applies them to stocks. The shared layers (transport, retry, rate-limit parsing, `ParallelArrays`, `JsonResponseParser`) are reused unchanged; there are **two additive shared-layer changes** — a tolerant date parser (`MarketDataDates.parseDateOrTimestampField`) and a per-response rate-limit accessor (`MarketDataResponse.rateLimit()`, §8.2). If you reviewed the options PR, the load-bearing shape will be familiar — focus your time on §4 (per-endpoint query translation), §5 (the per-endpoint required-column sets + the `news` deserializer), §7–§8 (batch vs. fan-out, news/earnings specifics), and the two stocks-/SDK-specific additions: **§12 candle auto-chunking** (§9.9) and **§8.2 per-response rate limits** (§9.10).
| Responses |`Stock*Response.java`, `MarketDataResponse.java`, `AbstractMarketDataResponse.java`|`values()` payload typing; `StockNewsResponse.updated()` scalar accessor; the new `rateLimit()` (§8.2) on the interface + base |
### 1.3 What changed in shared layers (vs. "unchanged")
76
+
### 1.3 What changed in shared layers (additive only)
76
77
77
-
-**`MarketDataDates`** gained `parseDateOrTimestampField` — additive; the existing `parseDateField` / `parseTimestampField` are untouched. Everything else (`ParallelArrays`, `JsonResponseParser`, `RequestConfig`, `MarketDataResponse`, transport/retry/rate-limit/status-cache/exceptions) is reused exactly as the options PR left it. Confirm no existing parser was modified.
78
+
-**`MarketDataDates`** gained `parseDateOrTimestampField` — the existing `parseDateField` / `parseTimestampField` are untouched. Confirm no existing parser was modified.
79
+
-**`MarketDataResponse`** gained `rateLimit()` (§8.2), implemented once in `AbstractMarketDataResponse` (it parses `RateLimitHeaders.parse(envelope.headers())` in the constructor). Since that base is the **only** implementor of the interface, every existing response type — options, utilities, `CsvResponse`, `HtmlResponse` — gets it with no per-type change. Confirm the interface addition didn't miss an implementor and that options/utilities tests still pass.
80
+
- Everything else (`ParallelArrays`, `JsonResponseParser`, `RequestConfig`, `RateLimitHeaders`, transport/retry/status-cache/exceptions) is reused exactly as the options PR left it.
78
81
79
82
---
80
83
81
84
## 2. The response model (reused)
82
85
83
-
No new model concepts — `Stock*Response` are thin subclasses of `AbstractMarketDataResponse<T>` (see [Options guide §2](OPTIONS_REVIEW_GUIDE.md#2-the-response-model) for the full shape). `values()` is the flat payload per endpoint (`List<StockCandle>`, `List<StockQuote>`, …). The one extra: `StockNewsResponse.updated()` exposes the feed's scalar update time (it sits at the response root, not on each row).
86
+
No new model concepts — `Stock*Response` are thin subclasses of `AbstractMarketDataResponse<T>` (see [Options guide §2](OPTIONS_REVIEW_GUIDE.md#2-the-response-model) for the full shape). `values()` is the flat payload per endpoint (`List<StockCandle>`, `List<StockQuote>`, …). The one endpoint extra: `StockNewsResponse.updated()` exposes the feed's scalar update time (it sits at the response root, not on each row).
84
87
85
-
What to check: `values()` return types match the wire shape per endpoint; `updated()` is only on `StockNewsResponse`.
88
+
**New SDK-wide accessor:**`MarketDataResponse.rateLimit()` returns the `RateLimitSnapshot` parsed from this response's own `x-api-ratelimit-*` headers (§8.2) — request-scoped, distinct from the client-level `client.getRateLimits()`. `null` when the four headers weren't all present. §9.10.
89
+
90
+
What to check: `values()` return types match the wire shape per endpoint; `updated()` is only on `StockNewsResponse`; `rateLimit()` is populated from the per-response headers (the merged-chunk candle response reflects the final slice's headers, §9.9).
| 9.6 |**`StockResolution` is a value type**| Not an enum — the API's resolution family is open-ended. Factories validate positivity; `of(String)` passes an arbitrary token through. |
| 9.8 |**`mode=cached` is quote-only**| The backend rejects `cached` on list endpoints (candles/news/earnings) — a consumer concern, not enforced by the SDK. |
202
+
| 9.9 |**Candle auto-chunking (§12)**| An intraday request with a `from` bound spanning > ~1 year is split into year-sized sub-requests (`candleChunks`), fanned out through the 50-permit pool and merged (`candlesAsync` typed; `StocksCsvResource.mergeCsvBodies` for CSV). `StockResolution.isIntraday()` is the trigger. When split, the merged response's metadata (status/json/`rateLimit`) reflects the final slice. |
203
+
| 9.10 |**Per-response rate limit (§8.2)**|`MarketDataResponse.rateLimit()` is parsed from each response's own `x-api-ratelimit-*` headers (in `AbstractMarketDataResponse`, SDK-wide) — request-scoped, distinct from client-level `getRateLimits()`. |
197
204
198
205
---
199
206
@@ -204,20 +211,22 @@ Do **not** flag as missing — deferred, documented in [`PR.md`](../PR.md):
204
211
-**ADR-008 accept + ADR-009**, and the `docs/java-sdk-requirements.md` per-resource section that depends on an accepted source ADR.
205
212
-**HTML facet exposure** — package-private until the backend serves `format=html`.
206
213
-**SDK-wide setter de-duplication** — the universal-param setters are copy-pasted per resource; a self-typed-base refactor is tracked for before v1.
207
-
-**§13 JaCoCo 100% threshold**, **§8 per-response rate-limit snapshot** — unchanged from the options PR.
214
+
-**§13 JaCoCo 100% threshold** — unchanged from the options PR.
208
215
-**`funds`/`markets`** — adopt this convention next.
209
216
210
217
---
211
218
212
219
## Reviewer checklist
213
220
214
-
-[ ] Shared layers untouched except the additive `MarketDataDates.parseDateOrTimestampField`; no existing parser modified.
221
+
-[ ] Shared layers changed only additively: `MarketDataDates.parseDateOrTimestampField` and `MarketDataResponse.rateLimit()` (implemented once in `AbstractMarketDataResponse`); no existing parser modified; options/utilities responses inherit `rateLimit()` with no per-type change.
215
222
-[ ] Requests: Builder per endpoint, no `String` overloads; `StockResolution` value-type factories validate; shared `validateWindow` (date XOR range, countback pairs with `to`).
216
223
-[ ] Query translation: every `*Spec` reads every getter; wire names correct (`52week`, `candle`, `adjustsplits`, …); `symbols` comma-joined; paths encoded.
217
224
-[ ] Nullable + Option A: every row field `@Nullable`; row builders lenient; per-endpoint **required-column sets** are right (quote 11-standard required, OHLC/52-week optional; earnings only symbol/date/updated required); strict-by-default preserved.
218
225
-[ ]`news` deserializer: per-row arrays + scalar `updated` (null on date-bounded); article fields strict; envelope handling.
219
226
-[ ]`quotes`/`prices` are a single batched request (one response, N rows) — not a fan-out map.
-[ ] Date handling: daily date-only vs. intraday full timestamp both decode; `updated` uses the plain timestamp parser.
222
-
-[ ] Unit (`./gradlew build`, 719 tests) and integration (`MARKETDATA_RUN_INTEGRATION_TESTS=true ./gradlew integrationTest`) green; `make demo-stocks` runs against the mock server.
229
+
-[ ] §12 candle chunking: `isIntraday()` classifier correct; only intraday + `from`-bounded ranges split; slices contiguous/non-overlapping (`to` exclusive); concurrent via the 50-permit pool; merged in chronological order; non-intraday / no-`from` stay single-request; CSV path merges with header dedup.
230
+
-[ ] §8.2 per-response rate limit: `rateLimit()` parsed from each response's headers; `null` when absent; request-scoped (distinct from `getRateLimits()`).
231
+
-[ ] Unit (`./gradlew build`, 728 tests) and integration (`MARKETDATA_RUN_INTEGRATION_TESTS=true ./gradlew integrationTest`) green; `make demo-stocks` runs against the mock server.
223
232
-[ ] Deferred items (§10) understood and not blocking.
0 commit comments