From b59ebed7f7544a6429a1c96e973a2e6bb9b6e48f Mon Sep 17 00:00:00 2001 From: Ben Papillon Date: Sat, 5 Sep 2026 18:25:02 -0700 Subject: [PATCH 1/5] chore(ci): sdk-e2e status check; testapp honors redisKeyPrefix Posts an sdk-e2e commit status: pending on update-wasm-v* bump branches until the schematic-api SDK E2E run reports back, success on every other PR, so sdk-e2e can be a required check without blocking normal PRs. The testapp passes redisKeyPrefix through so the E2E replicator-docs mode runs the README configuration. --- .github/workflows/sdk-e2e-status.yml | 50 ++++++++++++++++++++++++ sample-app/src/main/java/sample/App.java | 10 ++++- 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/sdk-e2e-status.yml diff --git a/.github/workflows/sdk-e2e-status.yml b/.github/workflows/sdk-e2e-status.yml new file mode 100644 index 0000000..f1e2076 --- /dev/null +++ b/.github/workflows/sdk-e2e-status.yml @@ -0,0 +1,50 @@ +name: sdk-e2e status + +# Makes `sdk-e2e` a commit status on every PR so it can be a required check +# without blocking ordinary PRs: +# +# - update-wasm-v* branches (rules engine bumps opened by schematic-bot from +# schematic-api's rulesengine_release.yml): `pending` until the SDK E2E run +# that the release workflow dispatches in schematic-api reports back +# (its report-status job posts success/failure to this same context). +# - every other PR: `success` immediately; SDK E2E is not required. +# +# A new push to a bump branch resets the status to pending; re-run schematic-api's +# sdk_e2e.yml with sdk-ref= to report on the new head. +# +# pull_request_target so the token can write statuses on fork PRs too. Nothing +# from the PR is checked out or executed here. + +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: {} + +jobs: + status: + runs-on: ubuntu-latest + permissions: + statuses: write + steps: + - name: Set sdk-e2e status + env: + GH_TOKEN: ${{ github.token }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + E2E_URL: https://github.com/SchematicHQ/schematic-api/actions/workflows/sdk_e2e.yml + run: | + case "$HEAD_REF" in + update-wasm-v*) + state=pending + description="Waiting for the SDK E2E run in schematic-api" + ;; + *) + state=success + description="Not a rules engine WASM bump; SDK E2E not required" + ;; + esac + echo "$HEAD_REF @ ${HEAD_SHA:0:8}: sdk-e2e=$state ($description)" + gh api "repos/$GITHUB_REPOSITORY/statuses/$HEAD_SHA" \ + -f state="$state" -f context=sdk-e2e \ + -f description="$description" -f target_url="$E2E_URL" > /dev/null diff --git a/sample-app/src/main/java/sample/App.java b/sample-app/src/main/java/sample/App.java index 75211c2..1a91c9a 100644 --- a/sample-app/src/main/java/sample/App.java +++ b/sample-app/src/main/java/sample/App.java @@ -101,6 +101,9 @@ private static void handleConfigure(HttpExchange exchange) throws IOException { boolean useDataStream = Boolean.TRUE.equals(config.get("useDataStream")); String redisUrl = (String) config.get("redisUrl"); String replicatorUrl = (String) config.get("replicatorUrl"); + // Optional Redis key prefix, as a README-following user would set it. In + // replicator mode it must match the keys the replicator writes ("schematic:"). + String redisKeyPrefix = (String) config.get("redisKeyPrefix"); // Parse flag defaults Map flagDefaults = new HashMap<>(); @@ -155,8 +158,11 @@ private static void handleConfigure(HttpExchange exchange) throws IOException { DatastreamOptions.builder().cacheTTL(Duration.ofMillis(CACHE_TTL_MS)); if (redisUrl != null) { - dsBuilder.redisCache( - RedisCacheConfig.builder().endpoint(redisUrl).build()); + RedisCacheConfig.Builder redisConfig = RedisCacheConfig.builder().endpoint(redisUrl); + if (redisKeyPrefix != null && !redisKeyPrefix.isEmpty()) { + redisConfig.keyPrefix(redisKeyPrefix); + } + dsBuilder.redisCache(redisConfig.build()); } if (replicatorUrl != null) { From 68f58bd0fcf9ad8455d4bf443292d454cacc067e Mon Sep 17 00:00:00 2001 From: Ben Papillon Date: Sat, 5 Sep 2026 18:39:29 -0700 Subject: [PATCH 2/5] chore(testapp): point replicator health check at /ready The sample app passed the bare replicator URL as the health URL. That is a 404, the SDK read it as not ready, and every replicator-mode flag check fell back to the REST API; the E2E replicator job passed on REST values. The strict replicator E2E mode (SchematicHQ/actions#565) caught it. --- sample-app/src/main/java/sample/App.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sample-app/src/main/java/sample/App.java b/sample-app/src/main/java/sample/App.java index 1a91c9a..64be83b 100644 --- a/sample-app/src/main/java/sample/App.java +++ b/sample-app/src/main/java/sample/App.java @@ -166,7 +166,11 @@ private static void handleConfigure(HttpExchange exchange) throws IOException { } if (replicatorUrl != null) { - dsBuilder.withReplicatorMode(replicatorUrl); + // The health URL is the replicator's readiness endpoint, as in the + // other testapps. The bare base URL is a 404, which the SDK reads + // as "not ready", so every check fell back to the REST API and + // replicator mode was never exercised here. + dsBuilder.withReplicatorMode(replicatorUrl + "/ready"); } builder.datastreamOptions(dsBuilder.build()); From ba9052b2cb78b696e58620c889fa0c02dd87b91d Mon Sep 17 00:00:00 2001 From: Ben Papillon Date: Sat, 5 Sep 2026 18:51:16 -0700 Subject: [PATCH 3/5] chore(testapp): log the SDK at debug in the sample app --- sample-app/src/main/java/sample/App.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sample-app/src/main/java/sample/App.java b/sample-app/src/main/java/sample/App.java index 64be83b..e1de7cd 100644 --- a/sample-app/src/main/java/sample/App.java +++ b/sample-app/src/main/java/sample/App.java @@ -22,6 +22,7 @@ import com.schematic.api.cache.LocalCache; import com.schematic.api.cache.RedisCacheConfig; import com.schematic.api.datastream.DatastreamOptions; +import com.schematic.api.logger.LogLevel; import com.schematic.api.types.CheckFlagRequestBody; import com.schematic.api.types.EventBodyIdentify; import com.schematic.api.types.EventBodyIdentifyCompany; @@ -123,7 +124,9 @@ private static void handleConfigure(HttpExchange exchange) throws IOException { } } - Schematic.Builder builder = Schematic.builder().apiKey(apiKey); + // Debug so the SDK's "falling back to API" and replicator health lines + // show up in the E2E job's test app log, as in the other testapps. + Schematic.Builder builder = Schematic.builder().apiKey(apiKey).logLevel(LogLevel.DEBUG); if (baseUrl != null) { builder.basePath(baseUrl); From 7115b8d7c1284c54c0b33f72ca6340b87d7e2762 Mon Sep 17 00:00:00 2001 From: Ben Papillon Date: Sat, 5 Sep 2026 19:00:15 -0700 Subject: [PATCH 4/5] chore(testapp): use default TTL for datastream entity caches Same fix as the other testapps: the short flag-check TTL on the entity caches expired the replicator-owned company entry after the SDK wrote it back on track, and every later check missed the company. --- sample-app/src/main/java/sample/App.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sample-app/src/main/java/sample/App.java b/sample-app/src/main/java/sample/App.java index e1de7cd..9212fd6 100644 --- a/sample-app/src/main/java/sample/App.java +++ b/sample-app/src/main/java/sample/App.java @@ -157,8 +157,12 @@ private static void handleConfigure(HttpExchange exchange) throws IOException { // DataStream configuration if (useDataStream) { - DatastreamOptions.Builder dsBuilder = - DatastreamOptions.builder().cacheTTL(Duration.ofMillis(CACHE_TTL_MS)); + // Entity caches keep the SDK default TTL, matching the other testapps. + // The short CACHE_TTL_MS is only for the flag-check cache above; in + // replicator mode the replicator owns the Redis entries and a short TTL + // on the SDK's write-back (track -> company metrics update) expires them, + // after which every check logs "Company not found in cache". + DatastreamOptions.Builder dsBuilder = DatastreamOptions.builder(); if (redisUrl != null) { RedisCacheConfig.Builder redisConfig = RedisCacheConfig.builder().endpoint(redisUrl); From 7bc402252219613f17c6e0959f6a8d5750ad87b4 Mon Sep 17 00:00:00 2001 From: Ben Papillon Date: Sat, 5 Sep 2026 22:07:41 -0700 Subject: [PATCH 5/5] chore: fernignore sdk-e2e-status workflow --- .fernignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.fernignore b/.fernignore index 8c55e60..670bab1 100644 --- a/.fernignore +++ b/.fernignore @@ -5,6 +5,7 @@ LICENSE README.md .github/CODEOWNERS .github/workflows/ci.yml +.github/workflows/sdk-e2e-status.yml WASM_VERSION .gitignore scripts/