Skip to content

initial Market Data Java SDK scaffold + CI#1

Merged
MarketDataDev03 merged 5 commits into
mainfrom
00_base_setup
May 5, 2026
Merged

initial Market Data Java SDK scaffold + CI#1
MarketDataDev03 merged 5 commits into
mainfrom
00_base_setup

Conversation

@MarketDataDev03

@MarketDataDev03 MarketDataDev03 commented May 4, 2026

Copy link
Copy Markdown
Collaborator

feat: initial Market Data Java SDK scaffold + CI

Branch 00_base_setupmain · 2 commits · 39 files

Initial scaffold for the Java SDK. No endpoints are implemented in this PR — it lays the foundation that endpoints will land on. Subsequent PRs will add endpoints one by one, each shipping with its corresponding integration tests against the live API (gated by MARKETDATA_RUN_INTEGRATION_TESTS=true).

What's included

  • Build: Gradle 8.12 (Kotlin DSL), JDK 17 toolchain (--release 17), Spotless (Google Java Format), JaCoCo, Vanniktech Maven Publish, separate source set for integration tests.
  • Public API: MarketDataClient with builder, shared HTTP/2 HttpClient, 50-permit concurrency semaphore, rate-limit accessor. RateLimits record. JSpecify @NullMarked across the entire public surface.
  • Exceptions: sealed MarketDataException hierarchy with the 7 canonical subtypes (AuthenticationError, BadRequestError, NotFoundError, RateLimitError, ServerError, NetworkError, ParseError), each carrying support context (requestId, requestUrl, statusCode, timestamp) and a getSupportInfo() helper.
  • Configuration: cascade explicit → MARKETDATA_* env var → .env → default. Defaults: https://api.marketdata.app, v1. Tokens are never logged verbatim (Tokens.redact).
  • CI (three workflows + Codecov):
    • pull-request.yml: every PR open / sync runs ./gradlew build on JDK 17 and uploads the JaCoCo report to Codecov.
    • pr-matrix-on-demand.yml: opt-in forward-compat run on JDK 21 and 25, triggered by a PR comment (see flow below).
    • main.yml: every push to main runs the full matrix {17, 21, 25}; the JDK 17 entry uploads coverage to Codecov, becoming the baseline that subsequent PRs are diffed against.
    • codecov.yml enforces the rules: PR coverage may not drop more than 5 pp vs base branch (project status), and code added by the PR must hit at least 70 % coverage (patch status).
  • Docs: README.md, CLAUDE.md, CHANGELOG.md, LICENSE (MIT).

How the CI flow works

The forward-compat matrix (JDK 21 + 25) is opt-in on PRs to keep regular iteration cheap, and mandatory at merge time:

Event Workflow JDKs
PR opened / branch updated while PR open pull-request.yml 17
Comment with /run-all-jdks, /jdk-matrix, or /test-all on an open PR pr-matrix-on-demand.yml 21, 25
Push to main (merge) main.yml 17, 21, 25

The on-demand workflow listens to the issue_comment event (PRs are issues from GitHub's API perspective) and:

  1. Filters: only fires when (a) the comment is on a PR, not a regular issue, and (b) the body contains one of the three accepted slash commands.
  2. Authorizes: rejects the run unless the commenter has write, maintain, or admin permission. Without this, anyone in the world commenting on a fork PR could burn our CI minutes or attempt to leak secrets via a malicious build.
  3. Acks: reacts 👀 to the trigger comment so the user knows it picked up the request.
  4. Resolves the PR's HEAD SHA via the pulls API and checks out exactly that commit (not the merge ref).
  5. Runs ./gradlew test -PtestJdk=21 and =25 in parallel (fail-fast: false).
  6. Posts a summary comment back on the PR with ✅/❌ and a link to the run.

A note on the "comment trigger doesn't fire from this branch" gotcha: GitHub always loads issue_comment workflows from the default branch's copy of the file. Until this PR is merged into main, the slash commands have no effect. That's by design — it prevents a malicious PR from shipping its own self-modifying CI.

Tests & coverage

16 tests, all passing. Coverage: 83.1 % lines, 98 % methods, 100 % on the exception package.

Architectural decisions

Follows ADRs 001–006 (already on main) and the foundational rules in the cross-language SDK Requirements doc (§1, §4–§7, §10, §12, §15–§16). Request-flow specifics (retry, rate-limit header parsing, wire-format decoding, endpoints, integration tests) are intentionally deferred and listed explicitly in CLAUDE.md. They will land in the follow-up PRs alongside each endpoint.

@socket-security

socket-security Bot commented May 4, 2026

Copy link
Copy Markdown

@MarketDataDev03 MarketDataDev03 changed the title 00 base setup initial Market Data Java SDK scaffold + CI May 4, 2026
@MarketDataDev03 MarketDataDev03 self-assigned this May 4, 2026
@codecov

codecov Bot commented May 4, 2026

Copy link
Copy Markdown

The author of this PR, MarketDataDev03, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

if (isPresent(fromSystem)) {
return fromSystem;
}
String fromDotEnv = readDotEnv().get(envKey);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be called once on the constructor to avoid re reading env vile on every resolve call

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I agree that repeated reading isn't ideal, but the actual cost is negligible: the file is small, the read only happens when building the client, and it takes microseconds per call.

However, after reviewing the code, it became clear that the static Configuration shape was the real problem. It blocks the ability to fully test. I'm going to rewrite it to fix the aforementioned problem and improve testability.

Comment thread .gitignore
# Gradle
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on README says this file is not commited and this is a rule with "!" to not exclude it, so the file was commited.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. The idea is to force it to always be committed (the standard convention in Gradle projects). The README is the one lying: it has an old paragraph from when the wrapper jar hadn't been generated yet, and I never updated it after committing. I'll fix it now.

@MarketDataDev03 MarketDataDev03 merged commit 2bd1bf5 into main May 5, 2026
5 checks passed
@MarketDataDev03 MarketDataDev03 deleted the 00_base_setup branch May 5, 2026 19:01
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.

2 participants