diff --git a/CHANGELOG.md b/CHANGELOG.md index f2dc942..3586b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +## [0.3.1] — 2026-05-25 + +### Added + +- `AuthApi.auth()` — exchange an API key for a short-lived JWT against `POST /auth/token` (OpenAPI spec 0.95.1). The client now ships `AuthTokenResponse` and `AuthTokenError` models and an `apiKeyAuth` (`X-API-Key`) security scheme. +- `EquityPoint` model and `ResultMap.pnlTotalPercent` / `ResultMap.equityCurve` fields, carried forward from a prior spec bump that had not been regenerated here. + +### Fixed + +- README quickstart now reads the JWT from `QTSURFER_TOKEN` (was `JWT_API_TOKEN`), matching the TS and Python clients. + ## [0.2.0] — 2026-05-17 ### Changed diff --git a/README.md b/README.md index fc51e3a..868b677 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Add the JitPack repository and the dependency: com.qtsurfer api-client-java - 0.2.0 + 0.3.0 ``` @@ -49,12 +49,12 @@ For Gradle: ```gradle repositories { maven { url 'https://jitpack.io' } } -dependencies { implementation 'com.qtsurfer:api-client:0.2.0' } +dependencies { implementation 'com.qtsurfer:api-client:0.3.0' } ``` ### Via Maven Central (future) -Once published to Central, the coordinate will be `com.qtsurfer:api-client:0.1.2`. +Once published to Central, the coordinate will be `com.qtsurfer:api-client:0.3.0`. ## Quick start @@ -66,18 +66,43 @@ import com.qtsurfer.api.client.model.Exchange; import java.util.List; ApiClient client = new ApiClient(); -client.updateBaseUri("https://api.qtsurfer.net/v1"); +client.updateBaseUri("https://api.qtsurfer.com/v1"); client.setRequestInterceptor(builder -> - builder.header("Authorization", "Bearer " + System.getenv("JWT_API_TOKEN"))); + builder.header("Authorization", "Bearer " + System.getenv("QTSURFER_TOKEN"))); ExchangeApi exchanges = new ExchangeApi(client); List result = exchanges.getExchanges(); ``` +### API key → JWT + +Every endpoint above expects a short-lived JWT in `Authorization: Bearer …`. +Exchange a long-lived API key for one via `AuthApi.auth()`: + +```java +import com.qtsurfer.api.client.api.AuthApi; +import com.qtsurfer.api.client.invoker.ApiClient; +import com.qtsurfer.api.client.model.AuthTokenResponse; + +ApiClient apikeyClient = new ApiClient(); +apikeyClient.updateBaseUri("https://api.qtsurfer.com/v1"); +apikeyClient.setRequestInterceptor(builder -> + builder.header("X-API-Key", System.getenv("QTSURFER_APIKEY"))); + +AuthTokenResponse token = new AuthApi(apikeyClient).auth(); +String jwt = token.getAccessToken(); // feed to a Bearer-authed ApiClient +``` + +For production use, prefer the [`com.qtsurfer:sdk`](https://github.com/QTSurfer/sdk-java) +`auth(apikey)` helper — it returns an `AuthenticatedClient` that refreshes the +JWT transparently, reads `QTSURFER_APIKEY` from the environment, and supports +pluggable token stores so callers don't reinvent that plumbing. + ## API surface | API class | Methods | | --- | --- | +| `AuthApi` | `auth()` — exchange API key for a short-lived JWT | | `ExchangeApi` | `getExchanges()`, `getInstruments(exchangeId)` | | `ExchangeBinaryDownloads` | `getTickersHour(...)`, `getKlinesHour(...)` — Lastra/Parquet streams (manual; see note below) | | `StrategyApi` | `postStrategy(body, xCompileAsync)`, `getStrategyStatus(strategyId)` | @@ -110,7 +135,7 @@ The class reuses the `ApiClient`'s `HttpClient` and request interceptor, so any `ApiClient` exposes the underlying `HttpClient.Builder` and an `ObjectMapper`, plus hooks for request/response interceptors. ```java -client.updateBaseUri("https://api.qtsurfer.net/v1"); +client.updateBaseUri("https://api.qtsurfer.com/v1"); client.setRequestInterceptor(builder -> builder.header("Authorization", "Bearer " + token) diff --git a/pom.xml b/pom.xml index 4e1ff64..04537be 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.qtsurfer api-client - 0.2.0 + 0.3.1 jar QTSurfer API Client